--- /dev/null
+{
+ "project_id" : "clang-tools-extra",
+ "conduit_uri" : "https://reviews.llvm.org/"
+}
--- /dev/null
+#==============================================================================#
+# This file specifies intentionally untracked files that git should ignore.
+# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
+#
+# This file is intentionally different from the output of `git svn show-ignore`,
+# as most of those are useless.
+#==============================================================================#
+
+#==============================================================================#
+# File extensions to be ignored anywhere in the tree.
+#==============================================================================#
+# Temp files created by most text editors.
+*~
+# Merge files created by git.
+*.orig
+# Byte compiled python modules.
+*.pyc
+# vim swap files
+.*.swp
+.sw?
+
+#==============================================================================#
+# Explicit files to ignore (only matches one).
+#==============================================================================#
+cscope.files
+cscope.out
+.clang_complete
+
+#==============================================================================#
+# Directories to ignore (do not add trailing '/'s, they skip symlinks).
+#==============================================================================#
+docs/_build
--- /dev/null
+add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-reorder-fields)
+add_subdirectory(modularize)
+if(CLANG_ENABLE_STATIC_ANALYZER)
+add_subdirectory(clang-tidy)
+add_subdirectory(clang-tidy-vs)
+endif()
+
+add_subdirectory(change-namespace)
+add_subdirectory(clang-query)
+add_subdirectory(clang-move)
+add_subdirectory(clangd)
+add_subdirectory(include-fixer)
+add_subdirectory(pp-trace)
+add_subdirectory(tool-template)
+
+# Add the common testsuite after all the tools.
+# TODO: Support tests with more granularity when features are off?
+if(CLANG_ENABLE_STATIC_ANALYZER AND CLANG_INCLUDE_TESTS)
+add_subdirectory(test)
+add_subdirectory(unittests)
+endif()
+
+option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
+ ${LLVM_INCLUDE_DOCS})
+if( CLANG_TOOLS_EXTRA_INCLUDE_DOCS )
+ add_subdirectory(docs)
+endif()
+
--- /dev/null
+This file is a list of the people responsible for ensuring that patches for a
+particular tool are reviewed, either by themself or by someone else. They are
+also the gatekeepers for their part of Clang, with the final word on what goes
+in or not.
+
+The list is sorted by surname and formatted to allow easy grepping and
+beautification by scripts. The fields are: name (N), email (E), web-address
+(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
+(S).
+
+N: Peter Collingbourne
+E: peter@pcc.me.uk
+D: clang-query
+
+N: Manuel Klimek
+E: klimek@google.com
+D: clang-rename, all parts of clang-tools-extra not covered by someone else
+
+N: Alexander Kornienko
+E: alexfh@google.com
+D: clang-tidy
--- /dev/null
+==============================================================================
+LLVM Release License
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+The LLVM software contains code written by third parties. Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
+The following pieces of software have additional or alternate copyrights,
+licenses, and/or restrictions:
+
+Program Directory
+------- ---------
+clang-tidy clang-tidy/cert
+clang-tidy clang-tidy/hicpp
--- /dev/null
+//===----------------------------------------------------------------------===//
+// Clang Tools repository
+//===----------------------------------------------------------------------===//
+
+Welcome to the repository of extra Clang Tools. This repository holds tools
+that are developed as part of the LLVM compiler infrastructure project and the
+Clang frontend. These tools are kept in a separate "extra" repository to
+allow lighter weight checkouts of the core Clang codebase.
+
+This repository is only intended to be checked out inside of a full LLVM+Clang
+tree, and in the 'tools/extra' subdirectory of the Clang checkout.
+
+All discussion regarding Clang, Clang-based tools, and code in this repository
+should be held using the standard Clang mailing lists:
+ http://lists.llvm.org/mailman/listinfo/cfe-dev
+
+Code review for this tree should take place on the standard Clang patch and
+commit lists:
+ http://lists.llvm.org/mailman/listinfo/cfe-commits
+
+If you find a bug in these tools, please file it in the LLVM bug tracker:
+ http://llvm.org/bugs/
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_library(clangChangeNamespace
+ ChangeNamespace.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangTooling
+ clangToolingCore
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "ChangeNamespace.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace change_namespace {
+
+namespace {
+
+inline std::string
+joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
+ if (Namespaces.empty())
+ return "";
+ std::string Result = Namespaces.front();
+ for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
+ Result += ("::" + *I).str();
+ return Result;
+}
+
+// Given "a::b::c", returns {"a", "b", "c"}.
+llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
+ llvm::SmallVector<llvm::StringRef, 4> Splitted;
+ Name.split(Splitted, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ return Splitted;
+}
+
+SourceLocation startLocationForType(TypeLoc TLoc) {
+ // For elaborated types (e.g. `struct a::A`) we want the portion after the
+ // `struct` but including the namespace qualifier, `a::`.
+ if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
+ NestedNameSpecifierLoc NestedNameSpecifier =
+ TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
+ if (NestedNameSpecifier.getNestedNameSpecifier())
+ return NestedNameSpecifier.getBeginLoc();
+ TLoc = TLoc.getNextTypeLoc();
+ }
+ return TLoc.getLocStart();
+}
+
+SourceLocation endLocationForType(TypeLoc TLoc) {
+ // Dig past any namespace or keyword qualifications.
+ while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
+ TLoc.getTypeLocClass() == TypeLoc::Qualified)
+ TLoc = TLoc.getNextTypeLoc();
+
+ // The location for template specializations (e.g. Foo<int>) includes the
+ // templated types in its location range. We want to restrict this to just
+ // before the `<` character.
+ if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
+ return TLoc.castAs<TemplateSpecializationTypeLoc>()
+ .getLAngleLoc()
+ .getLocWithOffset(-1);
+ return TLoc.getEndLoc();
+}
+
+// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
+// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
+// is empty, nullptr is returned.
+// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
+// the NamespaceDecl of namespace "a" will be returned.
+const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
+ llvm::StringRef PartialNsName) {
+ if (!InnerNs || PartialNsName.empty())
+ return nullptr;
+ const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
+ const auto *CurrentNs = InnerNs;
+ auto PartialNsNameSplitted = splitSymbolName(PartialNsName);
+ while (!PartialNsNameSplitted.empty()) {
+ // Get the inner-most namespace in CurrentContext.
+ while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
+ CurrentContext = CurrentContext->getParent();
+ if (!CurrentContext)
+ return nullptr;
+ CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
+ if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
+ return nullptr;
+ PartialNsNameSplitted.pop_back();
+ CurrentContext = CurrentContext->getParent();
+ }
+ return CurrentNs;
+}
+
+static std::unique_ptr<Lexer>
+getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Loc.isMacroID() &&
+ !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
+ return nullptr;
+ // Break down the source location.
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+ // Try to load the file buffer.
+ bool InvalidTemp = false;
+ llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+ if (InvalidTemp)
+ return nullptr;
+
+ const char *TokBegin = File.data() + LocInfo.second;
+ // Lex from the start of the given location.
+ return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
+ LangOpts, File.begin(), TokBegin, File.end());
+}
+
+// FIXME: get rid of this helper function if this is supported in clang-refactor
+// library.
+static SourceLocation getStartOfNextLine(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
+ if (!Lex.get())
+ return SourceLocation();
+ llvm::SmallVector<char, 16> Line;
+ // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+ Lex->setParsingPreprocessorDirective(true);
+ Lex->ReadToEndOfLine(&Line);
+ auto End = Loc.getLocWithOffset(Line.size());
+ return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
+ ? End
+ : End.getLocWithOffset(1);
+}
+
+// Returns `R` with new range that refers to code after `Replaces` being
+// applied.
+tooling::Replacement
+getReplacementInChangedCode(const tooling::Replacements &Replaces,
+ const tooling::Replacement &R) {
+ unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
+ unsigned NewEnd =
+ Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
+ return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
+ R.getReplacementText());
+}
+
+// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
+// applying all existing Replaces first if there is conflict.
+void addOrMergeReplacement(const tooling::Replacement &R,
+ tooling::Replacements *Replaces) {
+ auto Err = Replaces->add(R);
+ if (Err) {
+ llvm::consumeError(std::move(Err));
+ auto Replace = getReplacementInChangedCode(*Replaces, R);
+ *Replaces = Replaces->merge(tooling::Replacements(Replace));
+ }
+}
+
+tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
+ llvm::StringRef ReplacementText,
+ const SourceManager &SM) {
+ if (!Start.isValid() || !End.isValid()) {
+ llvm::errs() << "start or end location were invalid\n";
+ return tooling::Replacement();
+ }
+ if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
+ llvm::errs()
+ << "start or end location were in different macro expansions\n";
+ return tooling::Replacement();
+ }
+ Start = SM.getSpellingLoc(Start);
+ End = SM.getSpellingLoc(End);
+ if (SM.getFileID(Start) != SM.getFileID(End)) {
+ llvm::errs() << "start or end location were in different files\n";
+ return tooling::Replacement();
+ }
+ return tooling::Replacement(
+ SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
+ SM.getSpellingLoc(End)),
+ ReplacementText);
+}
+
+void addReplacementOrDie(
+ SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,
+ const SourceManager &SM,
+ std::map<std::string, tooling::Replacements> *FileToReplacements) {
+ const auto R = createReplacement(Start, End, ReplacementText, SM);
+ auto Err = (*FileToReplacements)[R.getFilePath()].add(R);
+ if (Err)
+ llvm_unreachable(llvm::toString(std::move(Err)).c_str());
+}
+
+tooling::Replacement createInsertion(SourceLocation Loc,
+ llvm::StringRef InsertText,
+ const SourceManager &SM) {
+ if (Loc.isInvalid()) {
+ llvm::errs() << "insert Location is invalid.\n";
+ return tooling::Replacement();
+ }
+ Loc = SM.getSpellingLoc(Loc);
+ return tooling::Replacement(SM, Loc, 0, InsertText);
+}
+
+// Returns the shortest qualified name for declaration `DeclName` in the
+// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
+// is "a::c::d", then "b::X" will be returned.
+// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns
+// "::b::X" instead of "b::X" since there will be a name conflict otherwise.
+// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
+// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
+// will have empty name.
+std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
+ llvm::StringRef NsName) {
+ DeclName = DeclName.ltrim(':');
+ NsName = NsName.ltrim(':');
+ if (DeclName.find(':') == llvm::StringRef::npos)
+ return DeclName;
+
+ auto NsNameSplitted = splitSymbolName(NsName);
+ auto DeclNsSplitted = splitSymbolName(DeclName);
+ llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val();
+ // If the Decl is in global namespace, there is no need to shorten it.
+ if (DeclNsSplitted.empty())
+ return UnqualifiedDeclName;
+ // If NsName is the global namespace, we can simply use the DeclName sans
+ // leading "::".
+ if (NsNameSplitted.empty())
+ return DeclName;
+
+ if (NsNameSplitted.front() != DeclNsSplitted.front()) {
+ // The DeclName must be fully-qualified, but we still need to decide if a
+ // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the
+ // `DeclName` is "b::X", then the reference must be qualified as "::b::X"
+ // to avoid conflict.
+ if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front()))
+ return ("::" + DeclName).str();
+ return DeclName;
+ }
+ // Since there is already an overlap namespace, we know that `DeclName` can be
+ // shortened, so we reduce the longest common prefix.
+ auto DeclI = DeclNsSplitted.begin();
+ auto DeclE = DeclNsSplitted.end();
+ auto NsI = NsNameSplitted.begin();
+ auto NsE = NsNameSplitted.end();
+ for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) {
+ }
+ return (DeclI == DeclE)
+ ? UnqualifiedDeclName.str()
+ : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName)
+ .str();
+}
+
+std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
+ if (Code.back() != '\n')
+ Code += "\n";
+ auto NsSplitted = splitSymbolName(NestedNs);
+ while (!NsSplitted.empty()) {
+ // FIXME: consider code style for comments.
+ Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
+ "} // namespace " + NsSplitted.back() + "\n")
+ .str();
+ NsSplitted.pop_back();
+ }
+ return Code;
+}
+
+// Returns true if \p D is a nested DeclContext in \p Context
+bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
+ while (D) {
+ if (D == Context)
+ return true;
+ D = D->getParent();
+ }
+ return false;
+}
+
+// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
+bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
+ const DeclContext *DeclCtx, SourceLocation Loc) {
+ SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocStart());
+ Loc = SM.getSpellingLoc(Loc);
+ return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
+ (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
+ isNestedDeclContext(DeclCtx, D->getDeclContext()));
+}
+
+// Given a qualified symbol name, returns true if the symbol will be
+// incorrectly qualified without leading "::".
+bool conflictInNamespace(llvm::StringRef QualifiedSymbol,
+ llvm::StringRef Namespace) {
+ auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":"));
+ assert(!SymbolSplitted.empty());
+ SymbolSplitted.pop_back(); // We are only interested in namespaces.
+
+ if (SymbolSplitted.size() > 1 && !Namespace.empty()) {
+ auto NsSplitted = splitSymbolName(Namespace.trim(":"));
+ assert(!NsSplitted.empty());
+ // We do not check the outermost namespace since it would not be a conflict
+ // if it equals to the symbol's outermost namespace and the symbol name
+ // would have been shortened.
+ for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) {
+ if (*I == SymbolSplitted.front())
+ return true;
+ }
+ }
+ return false;
+}
+
+AST_MATCHER(EnumDecl, isScoped) {
+ return Node.isScoped();
+}
+
+bool isTemplateParameter(TypeLoc Type) {
+ while (!Type.isNull()) {
+ if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
+ return true;
+ Type = Type.getNextTypeLoc();
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+ChangeNamespaceTool::ChangeNamespaceTool(
+ llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+ llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
+ std::map<std::string, tooling::Replacements> *FileToReplacements,
+ llvm::StringRef FallbackStyle)
+ : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
+ OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
+ FilePattern(FilePattern), FilePatternRE(FilePattern) {
+ FileToReplacements->clear();
+ auto OldNsSplitted = splitSymbolName(OldNamespace);
+ auto NewNsSplitted = splitSymbolName(NewNamespace);
+ // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
+ while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
+ OldNsSplitted.front() == NewNsSplitted.front()) {
+ OldNsSplitted.erase(OldNsSplitted.begin());
+ NewNsSplitted.erase(NewNsSplitted.begin());
+ }
+ DiffOldNamespace = joinNamespaces(OldNsSplitted);
+ DiffNewNamespace = joinNamespaces(NewNsSplitted);
+
+ for (const auto &Pattern : WhiteListedSymbolPatterns)
+ WhiteListedSymbolRegexes.emplace_back(Pattern);
+}
+
+void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
+ std::string FullOldNs = "::" + OldNamespace;
+ // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
+ // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
+ // be "a::b". Declarations in this namespace will not be visible in the new
+ // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
+ llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
+ llvm::StringRef(DiffOldNamespace)
+ .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,
+ /*KeepEmpty=*/false);
+ std::string Prefix = "-";
+ if (!DiffOldNsSplitted.empty())
+ Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
+ DiffOldNsSplitted.front())
+ .str();
+ auto IsInMovedNs =
+ allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
+ isExpansionInFileMatching(FilePattern));
+ auto IsVisibleInNewNs = anyOf(
+ IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
+ // Match using declarations.
+ Finder->addMatcher(
+ usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
+ .bind("using"),
+ this);
+ // Match using namespace declarations.
+ Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
+ IsVisibleInNewNs)
+ .bind("using_namespace"),
+ this);
+ // Match namespace alias declarations.
+ Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
+ IsVisibleInNewNs)
+ .bind("namespace_alias"),
+ this);
+
+ // Match old namespace blocks.
+ Finder->addMatcher(
+ namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
+ .bind("old_ns"),
+ this);
+
+ // Match class forward-declarations in the old namespace.
+ // Note that forward-declarations in classes are not matched.
+ Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),
+ IsInMovedNs, hasParent(namespaceDecl()))
+ .bind("class_fwd_decl"),
+ this);
+
+ // Match template class forward-declarations in the old namespace.
+ Finder->addMatcher(
+ classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),
+ IsInMovedNs, hasParent(namespaceDecl()))
+ .bind("template_class_fwd_decl"),
+ this);
+
+ // Match references to types that are not defined in the old namespace.
+ // Forward-declarations in the old namespace are also matched since they will
+ // be moved back to the old namespace.
+ auto DeclMatcher = namedDecl(
+ hasAncestor(namespaceDecl()),
+ unless(anyOf(
+ isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
+ hasAncestor(cxxRecordDecl()),
+ allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
+
+ // Using shadow declarations in classes always refers to base class, which
+ // does not need to be qualified since it can be inferred from inheritance.
+ // Note that this does not match using alias declarations.
+ auto UsingShadowDeclInClass =
+ usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));
+
+ // Match TypeLocs on the declaration. Carefully match only the outermost
+ // TypeLoc and template specialization arguments (which are not outermost)
+ // that are directly linked to types matching `DeclMatcher`. Nested name
+ // specifier locs are handled separately below.
+ Finder->addMatcher(
+ typeLoc(IsInMovedNs,
+ loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
+ unless(anyOf(hasParent(typeLoc(loc(qualType(
+ allOf(hasDeclaration(DeclMatcher),
+ unless(templateSpecializationType())))))),
+ hasParent(nestedNameSpecifierLoc()),
+ hasAncestor(isImplicit()),
+ hasAncestor(UsingShadowDeclInClass))),
+ hasAncestor(decl().bind("dc")))
+ .bind("type"),
+ this);
+
+ // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
+ // special case it.
+ // Since using declarations inside classes must have the base class in the
+ // nested name specifier, we leave it to the nested name specifier matcher.
+ Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),
+ unless(UsingShadowDeclInClass))
+ .bind("using_with_shadow"),
+ this);
+
+ // Handle types in nested name specifier. Specifiers that are in a TypeLoc
+ // matched above are not matched, e.g. "A::" in "A::A" is not matched since
+ // "A::A" would have already been fixed.
+ Finder->addMatcher(
+ nestedNameSpecifierLoc(
+ hasAncestor(decl(IsInMovedNs).bind("dc")),
+ loc(nestedNameSpecifier(
+ specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
+ unless(anyOf(hasAncestor(isImplicit()),
+ hasAncestor(UsingShadowDeclInClass),
+ hasAncestor(typeLoc(loc(qualType(hasDeclaration(
+ decl(equalsBoundNode("from_decl"))))))))))
+ .bind("nested_specifier_loc"),
+ this);
+
+ // Matches base class initializers in constructors. TypeLocs of base class
+ // initializers do not need to be fixed. For example,
+ // class X : public a::b::Y {
+ // public:
+ // X() : Y::Y() {} // Y::Y do not need namespace specifier.
+ // };
+ Finder->addMatcher(
+ cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);
+
+ // Handle function.
+ // Only handle functions that are defined in a namespace excluding member
+ // function, static methods (qualified by nested specifier), and functions
+ // defined in the global namespace.
+ // Note that the matcher does not exclude calls to out-of-line static method
+ // definitions, so we need to exclude them in the callback handler.
+ auto FuncMatcher =
+ functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
+ hasAncestor(namespaceDecl(isAnonymous())),
+ hasAncestor(cxxRecordDecl()))),
+ hasParent(namespaceDecl()));
+ Finder->addMatcher(decl(forEachDescendant(expr(anyOf(
+ callExpr(callee(FuncMatcher)).bind("call"),
+ declRefExpr(to(FuncMatcher.bind("func_decl")))
+ .bind("func_ref")))),
+ IsInMovedNs, unless(isImplicit()))
+ .bind("dc"),
+ this);
+
+ auto GlobalVarMatcher = varDecl(
+ hasGlobalStorage(), hasParent(namespaceDecl()),
+ unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
+ Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
+ to(GlobalVarMatcher.bind("var_decl")))
+ .bind("var_ref"),
+ this);
+
+ // Handle unscoped enum constant.
+ auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl(
+ hasParent(namespaceDecl()),
+ unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()),
+ hasAncestor(namespaceDecl(isAnonymous())))))));
+ Finder->addMatcher(
+ declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
+ to(UnscopedEnumMatcher.bind("enum_const_decl")))
+ .bind("enum_const_ref"),
+ this);
+}
+
+void ChangeNamespaceTool::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+ UsingDecls.insert(Using);
+ } else if (const auto *UsingNamespace =
+ Result.Nodes.getNodeAs<UsingDirectiveDecl>(
+ "using_namespace")) {
+ UsingNamespaceDecls.insert(UsingNamespace);
+ } else if (const auto *NamespaceAlias =
+ Result.Nodes.getNodeAs<NamespaceAliasDecl>(
+ "namespace_alias")) {
+ NamespaceAliasDecls.insert(NamespaceAlias);
+ } else if (const auto *NsDecl =
+ Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
+ moveOldNamespace(Result, NsDecl);
+ } else if (const auto *FwdDecl =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {
+ moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));
+ } else if (const auto *TemplateFwdDecl =
+ Result.Nodes.getNodeAs<ClassTemplateDecl>(
+ "template_class_fwd_decl")) {
+ moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));
+ } else if (const auto *UsingWithShadow =
+ Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
+ fixUsingShadowDecl(Result, UsingWithShadow);
+ } else if (const auto *Specifier =
+ Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
+ "nested_specifier_loc")) {
+ SourceLocation Start = Specifier->getBeginLoc();
+ SourceLocation End = endLocationForType(Specifier->getTypeLoc());
+ fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
+ } else if (const auto *BaseInitializer =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>(
+ "base_initializer")) {
+ BaseCtorInitializerTypeLocs.push_back(
+ BaseInitializer->getTypeSourceInfo()->getTypeLoc());
+ } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
+ // This avoids fixing types with record types as qualifier, which is not
+ // filtered by matchers in some cases, e.g. the type is templated. We should
+ // handle the record type qualifier instead.
+ TypeLoc Loc = *TLoc;
+ while (Loc.getTypeLocClass() == TypeLoc::Qualified)
+ Loc = Loc.getNextTypeLoc();
+ if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
+ NestedNameSpecifierLoc NestedNameSpecifier =
+ Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
+ const Type *SpecifierType =
+ NestedNameSpecifier.getNestedNameSpecifier()->getAsType();
+ if (SpecifierType && SpecifierType->isRecordType())
+ return;
+ }
+ fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
+ } else if (const auto *VarRef =
+ Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
+ const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
+ assert(Var);
+ if (Var->getCanonicalDecl()->isStaticDataMember())
+ return;
+ const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(Context && "Empty decl context.");
+ fixDeclRefExpr(Result, Context->getDeclContext(),
+ llvm::cast<NamedDecl>(Var), VarRef);
+ } else if (const auto *EnumConstRef =
+ Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
+ // Do not rename the reference if it is already scoped by the EnumDecl name.
+ if (EnumConstRef->hasQualifier() &&
+ EnumConstRef->getQualifier()->getKind() ==
+ NestedNameSpecifier::SpecifierKind::TypeSpec &&
+ EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
+ return;
+ const auto *EnumConstDecl =
+ Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
+ assert(EnumConstDecl);
+ const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(Context && "Empty decl context.");
+ // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it
+ // if it turns out to be an issue.
+ fixDeclRefExpr(Result, Context->getDeclContext(),
+ llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);
+ } else if (const auto *FuncRef =
+ Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
+ // If this reference has been processed as a function call, we do not
+ // process it again.
+ if (ProcessedFuncRefs.count(FuncRef))
+ return;
+ ProcessedFuncRefs.insert(FuncRef);
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
+ assert(Func);
+ const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(Context && "Empty decl context.");
+ fixDeclRefExpr(Result, Context->getDeclContext(),
+ llvm::cast<NamedDecl>(Func), FuncRef);
+ } else {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ assert(Call != nullptr && "Expecting callback for CallExpr.");
+ const auto *CalleeFuncRef =
+ llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
+ ProcessedFuncRefs.insert(CalleeFuncRef);
+ const FunctionDecl *Func = Call->getDirectCallee();
+ assert(Func != nullptr);
+ // FIXME: ignore overloaded operators. This would miss cases where operators
+ // are called by qualified names (i.e. "ns::operator <"). Ignore such
+ // cases for now.
+ if (Func->isOverloadedOperator())
+ return;
+ // Ignore out-of-line static methods since they will be handled by nested
+ // name specifiers.
+ if (Func->getCanonicalDecl()->getStorageClass() ==
+ StorageClass::SC_Static &&
+ Func->isOutOfLine())
+ return;
+ const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(Context && "Empty decl context.");
+ SourceRange CalleeRange = Call->getCallee()->getSourceRange();
+ replaceQualifiedSymbolInDeclContext(
+ Result, Context->getDeclContext(), CalleeRange.getBegin(),
+ CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
+ }
+}
+
+static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ std::unique_ptr<Lexer> Lex =
+ getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
+ assert(Lex.get() &&
+ "Failed to create lexer from the beginning of namespace.");
+ if (!Lex.get())
+ return SourceLocation();
+ Token Tok;
+ while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
+ }
+ return Tok.isNot(tok::TokenKind::l_brace)
+ ? SourceLocation()
+ : Tok.getEndLoc().getLocWithOffset(1);
+}
+
+// Stores information about a moved namespace in `MoveNamespaces` and leaves
+// the actual movement to `onEndOfTranslationUnit()`.
+void ChangeNamespaceTool::moveOldNamespace(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const NamespaceDecl *NsDecl) {
+ // If the namespace is empty, do nothing.
+ if (Decl::castToDeclContext(NsDecl)->decls_empty())
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+ // Get the range of the code in the old namespace.
+ SourceLocation Start =
+ getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());
+ assert(Start.isValid() && "Can't find l_brace for namespace.");
+ MoveNamespace MoveNs;
+ MoveNs.Offset = SM.getFileOffset(Start);
+ // The range of the moved namespace is from the location just past the left
+ // brace to the location right before the right brace.
+ MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;
+
+ // Insert the new namespace after `DiffOldNamespace`. For example, if
+ // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
+ // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
+ // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
+ // in the above example.
+ // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
+ // namespace will be a nested namespace in the old namespace.
+ const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
+ SourceLocation InsertionLoc = Start;
+ if (OuterNs) {
+ SourceLocation LocAfterNs = getStartOfNextLine(
+ OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());
+ assert(LocAfterNs.isValid() &&
+ "Failed to get location after DiffOldNamespace");
+ InsertionLoc = LocAfterNs;
+ }
+ MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));
+ MoveNs.FID = SM.getFileID(Start);
+ MoveNs.SourceMgr = Result.SourceManager;
+ MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs);
+}
+
+// Removes a class forward declaration from the code in the moved namespace and
+// creates an `InsertForwardDeclaration` to insert the forward declaration back
+// into the old namespace after moving code from the old namespace to the new
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+// namespace a {
+// class FWD;
+// class A { FWD *fwd; }
+// } // a
+// New code:
+// namespace a {
+// class FWD;
+// } // a
+// namespace x {
+// class A { a::FWD *fwd; }
+// } // x
+void ChangeNamespaceTool::moveClassForwardDeclaration(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const NamedDecl *FwdDecl) {
+ SourceLocation Start = FwdDecl->getLocStart();
+ SourceLocation End = FwdDecl->getLocEnd();
+ const SourceManager &SM = *Result.SourceManager;
+ SourceLocation AfterSemi = Lexer::findLocationAfterToken(
+ End, tok::semi, SM, Result.Context->getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true);
+ if (AfterSemi.isValid())
+ End = AfterSemi.getLocWithOffset(-1);
+ // Delete the forward declaration from the code to be moved.
+ addReplacementOrDie(Start, End, "", SM, &FileToReplacements);
+ llvm::StringRef Code = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
+ SM.getSpellingLoc(End)),
+ SM, Result.Context->getLangOpts());
+ // Insert the forward declaration back into the old namespace after moving the
+ // code from old namespace to new namespace.
+ // Insertion information is stored in `InsertFwdDecls` and actual
+ // insertion will be performed in `onEndOfTranslationUnit`.
+ // Get the (old) namespace that contains the forward declaration.
+ const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
+ // The namespace contains the forward declaration, so it must not be empty.
+ assert(!NsDecl->decls_empty());
+ const auto Insertion = createInsertion(
+ getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),
+ Code, SM);
+ InsertForwardDeclaration InsertFwd;
+ InsertFwd.InsertionOffset = Insertion.getOffset();
+ InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
+ InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
+}
+
+// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
+// FromDecl with the shortest qualified name possible when the reference is in
+// `NewNamespace`.
+void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
+ const NamedDecl *FromDecl) {
+ const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
+ if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
+ // This should not happen in usual unless the TypeLoc is in function type
+ // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
+ // `T` will be the translation unit. We simply use fully-qualified name
+ // here.
+ // Note that `FromDecl` must not be defined in the old namespace (according
+ // to `DeclMatcher`), so its fully-qualified name will not change after
+ // changing the namespace.
+ addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
+ *Result.SourceManager, &FileToReplacements);
+ return;
+ }
+ const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
+ // Calculate the name of the `NsDecl` after it is moved to new namespace.
+ std::string OldNs = NsDecl->getQualifiedNameAsString();
+ llvm::StringRef Postfix = OldNs;
+ bool Consumed = Postfix.consume_front(OldNamespace);
+ assert(Consumed && "Expect OldNS to start with OldNamespace.");
+ (void)Consumed;
+ const std::string NewNs = (NewNamespace + Postfix).str();
+
+ llvm::StringRef NestedName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ Result.SourceManager->getSpellingLoc(Start),
+ Result.SourceManager->getSpellingLoc(End)),
+ *Result.SourceManager, Result.Context->getLangOpts());
+ std::string FromDeclName = FromDecl->getQualifiedNameAsString();
+ for (llvm::Regex &RE : WhiteListedSymbolRegexes)
+ if (RE.match(FromDeclName))
+ return;
+ std::string ReplaceName =
+ getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
+ // Checks if there is any using namespace declarations that can shorten the
+ // qualified name.
+ for (const auto *UsingNamespace : UsingNamespaceDecls) {
+ if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
+ Start))
+ continue;
+ StringRef FromDeclNameRef = FromDeclName;
+ if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
+ ->getQualifiedNameAsString())) {
+ FromDeclNameRef = FromDeclNameRef.drop_front(2);
+ if (FromDeclNameRef.size() < ReplaceName.size())
+ ReplaceName = FromDeclNameRef;
+ }
+ }
+ // Checks if there is any namespace alias declarations that can shorten the
+ // qualified name.
+ for (const auto *NamespaceAlias : NamespaceAliasDecls) {
+ if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,
+ Start))
+ continue;
+ StringRef FromDeclNameRef = FromDeclName;
+ if (FromDeclNameRef.consume_front(
+ NamespaceAlias->getNamespace()->getQualifiedNameAsString() +
+ "::")) {
+ std::string AliasName = NamespaceAlias->getNameAsString();
+ std::string AliasQualifiedName =
+ NamespaceAlias->getQualifiedNameAsString();
+ // We only consider namespace aliases define in the global namepspace or
+ // in namespaces that are directly visible from the reference, i.e.
+ // ancestor of the `OldNs`. Note that declarations in ancestor namespaces
+ // but not visible in the new namespace is filtered out by
+ // "IsVisibleInNewNs" matcher.
+ if (AliasQualifiedName != AliasName) {
+ // The alias is defined in some namespace.
+ assert(StringRef(AliasQualifiedName).endswith("::" + AliasName));
+ llvm::StringRef AliasNs =
+ StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);
+ if (!llvm::StringRef(OldNs).startswith(AliasNs))
+ continue;
+ }
+ std::string NameWithAliasNamespace =
+ (AliasName + "::" + FromDeclNameRef).str();
+ if (NameWithAliasNamespace.size() < ReplaceName.size())
+ ReplaceName = NameWithAliasNamespace;
+ }
+ }
+ // Checks if there is any using shadow declarations that can shorten the
+ // qualified name.
+ bool Matched = false;
+ for (const UsingDecl *Using : UsingDecls) {
+ if (Matched)
+ break;
+ if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
+ for (const auto *UsingShadow : Using->shadows()) {
+ const auto *TargetDecl = UsingShadow->getTargetDecl();
+ if (TargetDecl->getQualifiedNameAsString() ==
+ FromDecl->getQualifiedNameAsString()) {
+ ReplaceName = FromDecl->getNameAsString();
+ Matched = true;
+ break;
+ }
+ }
+ }
+ }
+ // If the new nested name in the new namespace is the same as it was in the
+ // old namespace, we don't create replacement.
+ if (NestedName == ReplaceName ||
+ (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName))
+ return;
+ // If the reference need to be fully-qualified, add a leading "::" unless
+ // NewNamespace is the global namespace.
+ if (ReplaceName == FromDeclName && !NewNamespace.empty() &&
+ conflictInNamespace(ReplaceName, NewNamespace))
+ ReplaceName = "::" + ReplaceName;
+ addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
+ &FileToReplacements);
+}
+
+// Replace the [Start, End] of `Type` with the shortest qualified name when the
+// `Type` is in `NewNamespace`.
+void ChangeNamespaceTool::fixTypeLoc(
+ const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
+ SourceLocation End, TypeLoc Type) {
+ // FIXME: do not rename template parameter.
+ if (Start.isInvalid() || End.isInvalid())
+ return;
+ // Types of CXXCtorInitializers do not need to be fixed.
+ if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
+ return;
+ if (isTemplateParameter(Type))
+ return;
+ // The declaration which this TypeLoc refers to.
+ const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
+ // `hasDeclaration` gives underlying declaration, but if the type is
+ // a typedef type, we need to use the typedef type instead.
+ auto IsInMovedNs = [&](const NamedDecl *D) {
+ if (!llvm::StringRef(D->getQualifiedNameAsString())
+ .startswith(OldNamespace + "::"))
+ return false;
+ auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart());
+ if (ExpansionLoc.isInvalid())
+ return false;
+ llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
+ return FilePatternRE.match(Filename);
+ };
+ // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
+ // `Type` is an alias type, we make `FromDecl` the type alias declaration.
+ // Also, don't fix the \p Type if it refers to a type alias decl in the moved
+ // namespace since the alias decl will be moved along with the type reference.
+ if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
+ FromDecl = Typedef->getDecl();
+ if (IsInMovedNs(FromDecl))
+ return;
+ } else if (auto *TemplateType =
+ Type.getType()->getAs<TemplateSpecializationType>()) {
+ if (TemplateType->isTypeAlias()) {
+ FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
+ if (IsInMovedNs(FromDecl))
+ return;
+ }
+ }
+ const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(DeclCtx && "Empty decl context.");
+ replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
+ End, FromDecl);
+}
+
+void ChangeNamespaceTool::fixUsingShadowDecl(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const UsingDecl *UsingDeclaration) {
+ SourceLocation Start = UsingDeclaration->getLocStart();
+ SourceLocation End = UsingDeclaration->getLocEnd();
+ if (Start.isInvalid() || End.isInvalid())
+ return;
+
+ assert(UsingDeclaration->shadow_size() > 0);
+ // FIXME: it might not be always accurate to use the first using-decl.
+ const NamedDecl *TargetDecl =
+ UsingDeclaration->shadow_begin()->getTargetDecl();
+ std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
+ // FIXME: check if target_decl_name is in moved ns, which doesn't make much
+ // sense. If this happens, we need to use name with the new namespace.
+ // Use fully qualified name in UsingDecl for now.
+ addReplacementOrDie(Start, End, "using ::" + TargetDeclName,
+ *Result.SourceManager, &FileToReplacements);
+}
+
+void ChangeNamespaceTool::fixDeclRefExpr(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const DeclContext *UseContext, const NamedDecl *From,
+ const DeclRefExpr *Ref) {
+ SourceRange RefRange = Ref->getSourceRange();
+ replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
+ RefRange.getEnd(), From);
+}
+
+void ChangeNamespaceTool::onEndOfTranslationUnit() {
+ // Move namespace blocks and insert forward declaration to old namespace.
+ for (const auto &FileAndNsMoves : MoveNamespaces) {
+ auto &NsMoves = FileAndNsMoves.second;
+ if (NsMoves.empty())
+ continue;
+ const std::string &FilePath = FileAndNsMoves.first;
+ auto &Replaces = FileToReplacements[FilePath];
+ auto &SM = *NsMoves.begin()->SourceMgr;
+ llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
+ auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ continue;
+ }
+ // Replacements on the changed code for moving namespaces and inserting
+ // forward declarations to old namespaces.
+ tooling::Replacements NewReplacements;
+ // Cut the changed code from the old namespace and paste the code in the new
+ // namespace.
+ for (const auto &NsMove : NsMoves) {
+ // Calculate the range of the old namespace block in the changed
+ // code.
+ const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
+ const unsigned NewLength =
+ Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
+ NewOffset;
+ tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
+ std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
+ std::string MovedCodeWrappedInNewNs =
+ wrapCodeInNamespace(DiffNewNamespace, MovedCode);
+ // Calculate the new offset at which the code will be inserted in the
+ // changed code.
+ unsigned NewInsertionOffset =
+ Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
+ tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+ MovedCodeWrappedInNewNs);
+ addOrMergeReplacement(Deletion, &NewReplacements);
+ addOrMergeReplacement(Insertion, &NewReplacements);
+ }
+ // After moving namespaces, insert forward declarations back to old
+ // namespaces.
+ const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
+ for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
+ unsigned NewInsertionOffset =
+ Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
+ tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+ FwdDeclInsertion.ForwardDeclText);
+ addOrMergeReplacement(Insertion, &NewReplacements);
+ }
+ // Add replacements referring to the changed code to existing replacements,
+ // which refers to the original code.
+ Replaces = Replaces.merge(NewReplacements);
+ auto Style = format::getStyle("file", FilePath, FallbackStyle);
+ if (!Style) {
+ llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+ continue;
+ }
+ // Clean up old namespaces if there is nothing in it after moving.
+ auto CleanReplacements =
+ format::cleanupAroundReplacements(Code, Replaces, *Style);
+ if (!CleanReplacements) {
+ llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+ continue;
+ }
+ FileToReplacements[FilePath] = *CleanReplacements;
+ }
+
+ // Make sure we don't generate replacements for files that do not match
+ // FilePattern.
+ for (auto &Entry : FileToReplacements)
+ if (!FilePatternRE.match(Entry.first))
+ Entry.second.clear();
+}
+
+} // namespace change_namespace
+} // namespace clang
--- /dev/null
+//===-- ChangeNamespace.h -- Change namespace ------------------*- 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_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+
+namespace clang {
+namespace change_namespace {
+
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions. Classes/functions in the moved namespace will have new
+// namespaces while references to symbols (e.g. types, functions) which are not
+// defined in the changed namespace will be correctly qualified by prepending
+// namespace specifiers before them.
+// This will try to add shortest namespace specifiers possible. When a symbol
+// reference needs to be fully-qualified, this adds a "::" prefix to the
+// namespace specifiers unless the new namespace is the global namespace.
+// For classes, only classes that are declared/defined in the given namespace in
+// speficifed files will be moved: forward declarations will remain in the old
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+// namespace a {
+// class FWD;
+// class A { FWD *fwd; }
+// } // a
+// New code:
+// namespace a {
+// class FWD;
+// } // a
+// namespace x {
+// class A { ::a::FWD *fwd; }
+// } // x
+// FIXME: support moving typedef, enums across namespaces.
+class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
+ // files matching `FilePattern`.
+ ChangeNamespaceTool(
+ llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+ llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
+ std::map<std::string, tooling::Replacements> *FileToReplacements,
+ llvm::StringRef FallbackStyle = "LLVM");
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder);
+
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ // Moves the changed code in old namespaces but leaves class forward
+ // declarations behind.
+ void onEndOfTranslationUnit() override;
+
+private:
+ void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
+ const NamespaceDecl *NsDecl);
+
+ void moveClassForwardDeclaration(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const NamedDecl *FwdDecl);
+
+ void replaceQualifiedSymbolInDeclContext(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
+ const NamedDecl *FromDecl);
+
+ void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
+ SourceLocation Start, SourceLocation End, TypeLoc Type);
+
+ void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+ const UsingDecl *UsingDeclaration);
+
+ void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+ const DeclContext *UseContext, const NamedDecl *From,
+ const DeclRefExpr *Ref);
+
+ // Information about moving an old namespace.
+ struct MoveNamespace {
+ // The start offset of the namespace block being moved in the original
+ // code.
+ unsigned Offset;
+ // The length of the namespace block in the original code.
+ unsigned Length;
+ // The offset at which the new namespace block will be inserted in the
+ // original code.
+ unsigned InsertionOffset;
+ // The file in which the namespace is declared.
+ FileID FID;
+ SourceManager *SourceMgr;
+ };
+
+ // Information about inserting a class forward declaration.
+ struct InsertForwardDeclaration {
+ // The offset at while the forward declaration will be inserted in the
+ // original code.
+ unsigned InsertionOffset;
+ // The code to be inserted.
+ std::string ForwardDeclText;
+ };
+
+ std::string FallbackStyle;
+ // In match callbacks, this contains replacements for replacing `typeLoc`s in
+ // and deleting forward declarations in the moved namespace blocks.
+ // In `onEndOfTranslationUnit` callback, the previous added replacements are
+ // applied (on the moved namespace blocks), and then changed code in old
+ // namespaces re moved to new namespaces, and previously deleted forward
+ // declarations are inserted back to old namespaces, from which they are
+ // deleted.
+ std::map<std::string, tooling::Replacements> &FileToReplacements;
+ // A fully qualified name of the old namespace without "::" prefix, e.g.
+ // "a::b::c".
+ std::string OldNamespace;
+ // A fully qualified name of the new namespace without "::" prefix, e.g.
+ // "x::y::z".
+ std::string NewNamespace;
+ // The longest suffix in the old namespace that does not overlap the new
+ // namespace.
+ // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+ // "a::x::y", then `DiffOldNamespace` will be "b::c".
+ std::string DiffOldNamespace;
+ // The longest suffix in the new namespace that does not overlap the old
+ // namespace.
+ // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+ // "a::x::y", then `DiffNewNamespace` will be "x::y".
+ std::string DiffNewNamespace;
+ // A regex pattern that matches files to be processed.
+ std::string FilePattern;
+ llvm::Regex FilePatternRE;
+ // Information about moved namespaces grouped by file.
+ // Since we are modifying code in old namespaces (e.g. add namespace
+ // spedifiers) as well as moving them, we store information about namespaces
+ // to be moved and only move them after all modifications are finished (i.e.
+ // in `onEndOfTranslationUnit`).
+ std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
+ // Information about forward declaration insertions grouped by files.
+ // A class forward declaration is not moved, so it will be deleted from the
+ // moved code block and inserted back into the old namespace. The insertion
+ // will be done after removing the code from the old namespace and before
+ // inserting it to the new namespace.
+ std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
+ // Records all using declarations, which can be used to shorten namespace
+ // specifiers.
+ llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
+ // Records all using namespace declarations, which can be used to shorten
+ // namespace specifiers.
+ llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
+ // Records all namespace alias declarations, which can be used to shorten
+ // namespace specifiers.
+ llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
+ // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
+ // be fixed.
+ llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
+ // Since a DeclRefExpr for a function call can be matched twice (one as
+ // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
+ // been processed so that we don't handle them twice.
+ llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
+ // Patterns of symbol names whose references are not expected to be updated
+ // when changing namespaces around them.
+ std::vector<llvm::Regex> WhiteListedSymbolRegexes;
+};
+
+} // namespace change_namespace
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_executable(clang-change-namespace
+ ClangChangeNamespace.cpp
+ )
+target_link_libraries(clang-change-namespace
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangChangeNamespace
+ clangFormat
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ )
+
+install(TARGETS clang-change-namespace
+ RUNTIME DESTINATION bin)
--- /dev/null
+//===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions.
+//
+// Example: test.cc
+// namespace na {
+// class X {};
+// namespace nb {
+// class Y { X x; };
+// } // namespace nb
+// } // namespace na
+// To move the definition of class Y from namespace "na::nb" to "x::y", run:
+// clang-change-namespace --old_namespace "na::nb" \
+// --new_namespace "x::y" --file_pattern "test.cc" test.cc --
+// Output:
+// namespace na {
+// class X {};
+// } // namespace na
+// namespace x {
+// namespace y {
+// class Y { na::X x; };
+// } // namespace y
+// } // namespace x
+
+#include "ChangeNamespace.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
+
+cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
+ cl::desc("Old namespace."),
+ cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
+ cl::desc("New namespace."),
+ cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> FilePattern(
+ "file_pattern", cl::Required,
+ cl::desc("Only rename namespaces in files that match the given pattern."),
+ cl::cat(ChangeNamespaceCategory));
+
+cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
+ cl::cat(ChangeNamespaceCategory));
+
+cl::opt<bool>
+ DumpYAML("dump_result",
+ cl::desc("Dump new file contents in YAML, if specified."),
+ cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> Style("style",
+ cl::desc("The style name used for reformatting."),
+ cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> WhiteListFile(
+ "whitelist_file",
+ cl::desc("A file containing regexes of symbol names that are not expected "
+ "to be updated when changing namespaces around them."),
+ cl::init(""), cl::cat(ChangeNamespaceCategory));
+
+llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() {
+ std::vector<std::string> Patterns;
+ if (WhiteListFile.empty())
+ return Patterns;
+
+ llvm::SmallVector<StringRef, 8> Lines;
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
+ llvm::MemoryBuffer::getFile(WhiteListFile);
+ if (!File)
+ return File.getError();
+ llvm::StringRef Content = File.get()->getBuffer();
+ Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ for (auto Line : Lines)
+ Patterns.push_back(Line.trim());
+ return Patterns;
+}
+
+} // anonymous namespace
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+ tooling::CommonOptionsParser OptionsParser(argc, argv,
+ ChangeNamespaceCategory);
+ const auto &Files = OptionsParser.getSourcePathList();
+ tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
+ llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns =
+ GetWhiteListedSymbolPatterns();
+ if (!WhiteListPatterns) {
+ llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". "
+ << WhiteListPatterns.getError().message() << "\n";
+ return 1;
+ }
+ change_namespace::ChangeNamespaceTool NamespaceTool(
+ OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns,
+ &Tool.getReplacements(), Style);
+ ast_matchers::MatchFinder Finder;
+ NamespaceTool.registerMatchers(&Finder);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&Finder);
+
+ if (int Result = Tool.run(Factory.get()))
+ return Result;
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+ auto &FileMgr = Tool.getFiles();
+ SourceManager Sources(Diagnostics, FileMgr);
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+
+ if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+ llvm::errs() << "Failed applying all replacements.\n";
+ return 1;
+ }
+ if (Inplace)
+ return Rewrite.overwriteChangedFiles();
+
+ std::set<llvm::StringRef> ChangedFiles;
+ for (const auto &it : Tool.getReplacements())
+ ChangedFiles.insert(it.first);
+
+ if (DumpYAML) {
+ auto WriteToYAML = [&](llvm::raw_ostream &OS) {
+ OS << "[\n";
+ for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
+ OS << " {\n";
+ OS << " \"FilePath\": \"" << *I << "\",\n";
+ const auto *Entry = FileMgr.getFile(*I);
+ auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+ std::string Content;
+ llvm::raw_string_ostream ContentStream(Content);
+ Rewrite.getEditBuffer(ID).write(ContentStream);
+ OS << " \"SourceText\": \""
+ << llvm::yaml::escape(ContentStream.str()) << "\"\n";
+ OS << " }";
+ if (I != std::prev(E))
+ OS << ",\n";
+ }
+ OS << "\n]\n";
+ };
+ WriteToYAML(llvm::outs());
+ return 0;
+ }
+
+ for (const auto &File : ChangedFiles) {
+ const auto *Entry = FileMgr.getFile(File);
+
+ auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+ outs() << "============== " << File << " ==============\n";
+ Rewrite.getEditBuffer(ID).write(llvm::outs());
+ outs() << "\n============================================\n";
+ }
+
+ return 0;
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangApplyReplacements
+ lib/Tooling/ApplyReplacements.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangRewrite
+ clangToolingCore
+ )
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ include
+ )
+add_subdirectory(tool)
--- /dev/null
+//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the interface for deduplicating, detecting
+/// conflicts in, and applying collections of Replacements.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
+#define LLVM_CLANG_APPLYREPLACEMENTS_H
+
+#include "clang/Tooling/Core/Diagnostic.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace clang {
+
+class DiagnosticsEngine;
+class Rewriter;
+
+namespace format {
+struct FormatStyle;
+} // end namespace format
+
+namespace replace {
+
+/// \brief Collection of source ranges.
+typedef std::vector<clang::tooling::Range> RangeVector;
+
+/// \brief Collection of TranslationUnitReplacements.
+typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
+
+/// \brief Collection of TranslationUnitReplacement files.
+typedef std::vector<std::string> TUReplacementFiles;
+
+/// \brief Collection of TranslationUniDiagnostics.
+typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
+
+/// \brief Map mapping file name to Replacements targeting that file.
+typedef llvm::DenseMap<const clang::FileEntry *,
+ std::vector<clang::tooling::Replacement>>
+ FileToReplacementsMap;
+
+/// \brief Recursively descends through a directory structure rooted at \p
+/// Directory and attempts to deserialize *.yaml files as
+/// TranslationUnitReplacements. All docs that successfully deserialize are
+/// added to \p TUs.
+///
+/// Directories starting with '.' are ignored during traversal.
+///
+/// \param[in] Directory Directory to begin search for serialized
+/// TranslationUnitReplacements.
+/// \param[out] TUs Collection of all found and deserialized
+/// TranslationUnitReplacements or TranslationUnitDiagnostics.
+/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
+/// found in \c Directory.
+/// \param[in] Diagnostics DiagnosticsEngine used for error output.
+///
+/// \returns An error_code indicating success or failure in navigating the
+/// directory structure.
+std::error_code collectReplacementsFromDirectory(
+ const llvm::StringRef Directory, TUReplacements &TUs,
+ TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
+
+std::error_code collectReplacementsFromDirectory(
+ const llvm::StringRef Directory, TUDiagnostics &TUs,
+ TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
+
+/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
+/// in \c TUs. If conflicts occur, no Replacements are applied.
+///
+/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
+/// value[i+1].getOffset().
+///
+/// \param[in] TUs Collection of TranslationUnitReplacements or
+/// TranslationUnitDiagnostics to merge,
+/// deduplicate, and test for conflicts.
+/// \param[out] GroupedReplacements Container grouping all Replacements by the
+/// file they target.
+/// \param[in] SM SourceManager required for conflict reporting.
+///
+/// \returns \parblock
+/// \li true If all changes were applied successfully.
+/// \li false If there were conflicts.
+bool mergeAndDeduplicate(const TUReplacements &TUs,
+ FileToReplacementsMap &GroupedReplacements,
+ clang::SourceManager &SM);
+
+bool mergeAndDeduplicate(const TUDiagnostics &TUs,
+ FileToReplacementsMap &GroupedReplacements,
+ clang::SourceManager &SM);
+
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+ Rewriter &Rewrite);
+
+/// \brief Apply all replacements in \c GroupedReplacements.
+///
+/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
+/// to apply.
+/// \param[out] Rewrites The results of applying replacements will be applied
+/// to this Rewriter.
+///
+/// \returns \parblock
+/// \li true If all changes were applied successfully.
+/// \li false If a replacement failed to apply.
+bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
+ clang::Rewriter &Rewrites);
+
+/// \brief Given a collection of Replacements for a single file, produces a list
+/// of source ranges that enclose those Replacements.
+///
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements from a single file.
+///
+/// \returns Collection of source ranges that enclose all given Replacements.
+/// One range is created for each replacement.
+RangeVector calculateChangedRanges(
+ const std::vector<clang::tooling::Replacement> &Replacements);
+
+/// \brief Write the contents of \c FileContents to disk. Keys of the map are
+/// filenames and values are the new contents for those files.
+///
+/// \param[in] Rewrites Rewriter containing written files to write to disk.
+bool writeFiles(const clang::Rewriter &Rewrites);
+
+/// \brief Delete the replacement files.
+///
+/// \param[in] Files Replacement files to delete.
+/// \param[in] Diagnostics DiagnosticsEngine used for error output.
+///
+/// \returns \parblock
+/// \li true If all files have been deleted successfully.
+/// \li false If at least one or more failures occur when deleting
+/// files.
+bool deleteReplacementFiles(const TUReplacementFiles &Files,
+ clang::DiagnosticsEngine &Diagnostics);
+
+} // end namespace replace
+} // end namespace clang
+
+#endif // LLVM_CLANG_APPLYREPLACEMENTS_H
--- /dev/null
+//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the implementation for deduplicating, detecting
+/// conflicts in, and applying collections of Replacements.
+///
+/// FIXME: Use Diagnostics for output instead of llvm::errs().
+///
+//===----------------------------------------------------------------------===//
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/DiagnosticsYaml.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+
+static void eatDiagnostics(const SMDiagnostic &, void *) {}
+
+namespace clang {
+namespace replace {
+
+std::error_code collectReplacementsFromDirectory(
+ const llvm::StringRef Directory, TUReplacements &TUs,
+ TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
+ using namespace llvm::sys::fs;
+ using namespace llvm::sys::path;
+
+ std::error_code ErrorCode;
+
+ for (recursive_directory_iterator I(Directory, ErrorCode), E;
+ I != E && !ErrorCode; I.increment(ErrorCode)) {
+ if (filename(I->path())[0] == '.') {
+ // Indicate not to descend into directories beginning with '.'
+ I.no_push();
+ continue;
+ }
+
+ if (extension(I->path()) != ".yaml")
+ continue;
+
+ TUFiles.push_back(I->path());
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
+ MemoryBuffer::getFile(I->path());
+ if (std::error_code BufferError = Out.getError()) {
+ errs() << "Error reading " << I->path() << ": " << BufferError.message()
+ << "\n";
+ continue;
+ }
+
+ yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
+ tooling::TranslationUnitReplacements TU;
+ YIn >> TU;
+ if (YIn.error()) {
+ // File doesn't appear to be a header change description. Ignore it.
+ continue;
+ }
+
+ // Only keep files that properly parse.
+ TUs.push_back(TU);
+ }
+
+ return ErrorCode;
+}
+
+std::error_code
+collectReplacementsFromDirectory(const llvm::StringRef Directory,
+ TUDiagnostics &TUs, TUReplacementFiles &TUFiles,
+ clang::DiagnosticsEngine &Diagnostics) {
+ using namespace llvm::sys::fs;
+ using namespace llvm::sys::path;
+
+ std::error_code ErrorCode;
+
+ for (recursive_directory_iterator I(Directory, ErrorCode), E;
+ I != E && !ErrorCode; I.increment(ErrorCode)) {
+ if (filename(I->path())[0] == '.') {
+ // Indicate not to descend into directories beginning with '.'
+ I.no_push();
+ continue;
+ }
+
+ if (extension(I->path()) != ".yaml")
+ continue;
+
+ TUFiles.push_back(I->path());
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
+ MemoryBuffer::getFile(I->path());
+ if (std::error_code BufferError = Out.getError()) {
+ errs() << "Error reading " << I->path() << ": " << BufferError.message()
+ << "\n";
+ continue;
+ }
+
+ yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
+ tooling::TranslationUnitDiagnostics TU;
+ YIn >> TU;
+ if (YIn.error()) {
+ // File doesn't appear to be a header change description. Ignore it.
+ continue;
+ }
+
+ // Only keep files that properly parse.
+ TUs.push_back(TU);
+ }
+
+ return ErrorCode;
+}
+
+/// \brief Dumps information for a sequence of conflicting Replacements.
+///
+/// \param[in] File FileEntry for the file the conflicting Replacements are
+/// for.
+/// \param[in] ConflictingReplacements List of conflicting Replacements.
+/// \param[in] SM SourceManager used for reporting.
+static void reportConflict(
+ const FileEntry *File,
+ const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
+ SourceManager &SM) {
+ FileID FID = SM.translateFile(File);
+ if (FID.isInvalid())
+ FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
+
+ // FIXME: Output something a little more user-friendly (e.g. unified diff?)
+ errs() << "The following changes conflict:\n";
+ for (const tooling::Replacement &R : ConflictingReplacements) {
+ if (R.getLength() == 0) {
+ errs() << " Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
+ << SM.getColumnNumber(FID, R.getOffset()) << " "
+ << R.getReplacementText() << "\n";
+ } else {
+ if (R.getReplacementText().empty())
+ errs() << " Remove ";
+ else
+ errs() << " Replace ";
+
+ errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
+ << SM.getColumnNumber(FID, R.getOffset()) << "-"
+ << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
+ << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
+
+ if (R.getReplacementText().empty())
+ errs() << "\n";
+ else
+ errs() << " with \"" << R.getReplacementText() << "\"\n";
+ }
+ }
+}
+
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+ Rewriter &Rewrite) {
+ bool Result = true;
+ for (auto 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<Replacement>
+// is replaced with tooling::Replacements class.
+static void deduplicate(std::vector<tooling::Replacement> &Replaces,
+ std::vector<tooling::Range> &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.
+///
+/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in,out] Replacements Container of all replacements grouped by file
+/// to be deduplicated and checked for conflicts.
+/// \param[in] SM SourceManager required for conflict reporting.
+///
+/// \returns \parblock
+/// \li true if conflicts were detected
+/// \li false if no conflicts were detected
+static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
+ SourceManager &SM) {
+ bool conflictsFound = false;
+
+ for (auto &FileAndReplacements : Replacements) {
+ const FileEntry *Entry = FileAndReplacements.first;
+ auto &Replacements = FileAndReplacements.second;
+ assert(Entry != nullptr && "No file entry!");
+
+ std::vector<tooling::Range> Conflicts;
+ deduplicate(FileAndReplacements.second, Conflicts);
+
+ if (Conflicts.empty())
+ continue;
+
+ conflictsFound = true;
+
+ errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
+
+ for (const tooling::Range &Conflict : Conflicts) {
+ auto ConflictingReplacements = llvm::makeArrayRef(
+ &Replacements[Conflict.getOffset()], Conflict.getLength());
+ reportConflict(Entry, ConflictingReplacements, SM);
+ }
+ }
+
+ return conflictsFound;
+}
+
+bool mergeAndDeduplicate(const TUReplacements &TUs,
+ FileToReplacementsMap &GroupedReplacements,
+ clang::SourceManager &SM) {
+
+ // Group all replacements by target file.
+ std::set<StringRef> Warned;
+ for (const auto &TU : TUs) {
+ for (const tooling::Replacement &R : TU.Replacements) {
+ // Use the file manager to deduplicate paths. FileEntries are
+ // automatically canonicalized.
+ const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
+ if (!Entry && Warned.insert(R.getFilePath()).second) {
+ errs() << "Described file '" << R.getFilePath()
+ << "' doesn't exist. Ignoring...\n";
+ continue;
+ }
+ GroupedReplacements[Entry].push_back(R);
+ }
+ }
+
+ // Ask clang to deduplicate and report conflicts.
+ return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
+}
+
+bool mergeAndDeduplicate(const TUDiagnostics &TUs,
+ FileToReplacementsMap &GroupedReplacements,
+ clang::SourceManager &SM) {
+
+ // Group all replacements by target file.
+ std::set<StringRef> Warned;
+ for (const auto &TU : TUs) {
+ for (const auto &D : TU.Diagnostics) {
+ for (const auto &Fix : D.Fix) {
+ for (const tooling::Replacement &R : Fix.second) {
+ // Use the file manager to deduplicate paths. FileEntries are
+ // automatically canonicalized.
+ const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
+ if (!Entry && Warned.insert(R.getFilePath()).second) {
+ errs() << "Described file '" << R.getFilePath()
+ << "' doesn't exist. Ignoring...\n";
+ continue;
+ }
+ GroupedReplacements[Entry].push_back(R);
+ }
+ }
+ }
+ }
+
+ // Ask clang to deduplicate and report conflicts.
+ return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
+}
+
+bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
+ clang::Rewriter &Rewrites) {
+
+ // Apply all changes
+ //
+ // FIXME: No longer certain GroupedReplacements is really the best kind of
+ // data structure for applying replacements. Rewriter certainly doesn't care.
+ // However, until we nail down the design of ReplacementGroups, might as well
+ // leave this as is.
+ for (const auto &FileAndReplacements : GroupedReplacements) {
+ if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
+ return false;
+ }
+
+ return true;
+}
+
+RangeVector calculateChangedRanges(
+ const std::vector<clang::tooling::Replacement> &Replaces) {
+ RangeVector ChangedRanges;
+
+ // Generate the new ranges from the replacements.
+ int Shift = 0;
+ for (const tooling::Replacement &R : Replaces) {
+ unsigned Offset = R.getOffset() + Shift;
+ unsigned Length = R.getReplacementText().size();
+ Shift += Length - R.getLength();
+ ChangedRanges.push_back(tooling::Range(Offset, Length));
+ }
+
+ return ChangedRanges;
+}
+
+bool writeFiles(const clang::Rewriter &Rewrites) {
+
+ for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
+ BufferI != BufferE; ++BufferI) {
+ StringRef FileName =
+ Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
+
+ std::error_code EC;
+ llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ errs() << "Warning: Could not write to " << EC.message() << "\n";
+ continue;
+ }
+ BufferI->second.write(FileStream);
+ }
+
+ return true;
+}
+
+bool deleteReplacementFiles(const TUReplacementFiles &Files,
+ clang::DiagnosticsEngine &Diagnostics) {
+ bool Success = true;
+ for (const auto &Filename : Files) {
+ std::error_code Error = llvm::sys::fs::remove(Filename);
+ if (Error) {
+ Success = false;
+ // FIXME: Use Diagnostics for outputting errors.
+ errs() << "Error deleting file: " << Filename << "\n";
+ errs() << Error.message() << "\n";
+ errs() << "Please delete the file manually\n";
+ }
+ }
+ return Success;
+}
+
+} // end namespace replace
+} // end namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_executable(clang-apply-replacements
+ ClangApplyReplacementsMain.cpp
+ )
+target_link_libraries(clang-apply-replacements
+ clangApplyReplacements
+ clangBasic
+ clangFormat
+ clangRewrite
+ clangToolingCore
+ )
+
+install(TARGETS clang-apply-replacements
+ RUNTIME DESTINATION bin)
--- /dev/null
+//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the main function for the
+/// clang-apply-replacements tool.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/Format/Format.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::replace;
+
+static cl::opt<std::string> Directory(cl::Positional, cl::Required,
+ cl::desc("<Search Root Directory>"));
+
+static cl::OptionCategory ReplacementCategory("Replacement Options");
+static cl::OptionCategory FormattingCategory("Formatting Options");
+
+const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
+ &FormattingCategory};
+
+static cl::opt<bool> RemoveTUReplacementFiles(
+ "remove-change-desc-files",
+ cl::desc("Remove the change description files regardless of successful\n"
+ "merging/replacing."),
+ cl::init(false), cl::cat(ReplacementCategory));
+
+static cl::opt<bool> DoFormat(
+ "format",
+ cl::desc("Enable formatting of code changed by applying replacements.\n"
+ "Use -style to choose formatting style.\n"),
+ cl::cat(FormattingCategory));
+
+// FIXME: Consider making the default behaviour for finding a style
+// configuration file to start the search anew for every file being changed to
+// handle situations where the style is different for different parts of a
+// project.
+
+static cl::opt<std::string> FormatStyleConfig(
+ "style-config",
+ cl::desc("Path to a directory containing a .clang-format file\n"
+ "describing a formatting style to use for formatting\n"
+ "code when -style=file.\n"),
+ cl::init(""), cl::cat(FormattingCategory));
+
+static cl::opt<std::string>
+ FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
+ cl::init("LLVM"), cl::cat(FormattingCategory));
+
+namespace {
+// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
+// "remove-change-desc-files" command line option) when exiting current scope.
+class ScopedFileRemover {
+public:
+ ScopedFileRemover(const TUReplacementFiles &Files,
+ clang::DiagnosticsEngine &Diagnostics)
+ : TURFiles(Files), Diag(Diagnostics) {}
+
+ ~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
+
+private:
+ const TUReplacementFiles &TURFiles;
+ clang::DiagnosticsEngine &Diag;
+};
+} // namespace
+
+static void printVersion() {
+ outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
+}
+
+/// \brief Convenience function to get rewritten content for \c Filename from
+/// \c Rewrites.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply
+/// \param[in] Rewrites Rewriter to use to apply replacements.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+///
+/// \returns \parblock
+/// \li true if all replacements were applied successfully.
+/// \li false if at least one replacement failed to apply.
+static bool
+getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
+ Rewriter &Rewrites, std::string &Result) {
+ if (Replacements.empty())
+ return true;
+
+ if (!applyAllReplacements(Replacements, Rewrites))
+ return false;
+
+ SourceManager &SM = Rewrites.getSourceMgr();
+ FileManager &Files = SM.getFileManager();
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ const clang::FileEntry *Entry = Files.getFile(FileName);
+ assert(Entry && "Expected an existing file");
+ FileID ID = SM.translateFile(Entry);
+ assert(ID.isValid() && "Expected a valid FileID");
+ const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
+ Result = std::string(Buffer->begin(), Buffer->end());
+
+ return true;
+}
+
+/// \brief Apply \c Replacements and return the new file contents.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \parblock
+/// \li true if all replacements applied successfully.
+/// \li false if at least one replacement failed to apply.
+static bool
+applyReplacements(const std::vector<tooling::Replacement> &Replacements,
+ std::string &Result, DiagnosticsEngine &Diagnostics) {
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(Replacements, Rewrites, Result);
+}
+
+/// \brief Apply code formatting to all places where replacements were made.
+///
+/// \pre !Replacements.empty().
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements that were made to the file. Provided
+/// to indicate where changes were made.
+/// \param[in] FileData The contents of the file \b after \c Replacements have
+/// been applied.
+/// \param[out] FormattedFileData The contents of the file after reformatting.
+/// \param[in] FormatStyle Style to apply.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \parblock
+/// \li true if reformatting replacements were all successfully
+/// applied.
+/// \li false if at least one reformatting replacement failed to apply.
+static bool
+applyFormatting(const std::vector<tooling::Replacement> &Replacements,
+ const StringRef FileData, std::string &FormattedFileData,
+ const format::FormatStyle &FormatStyle,
+ DiagnosticsEngine &Diagnostics) {
+ assert(!Replacements.empty() && "Need at least one replacement");
+
+ RangeVector Ranges = calculateChangedRanges(Replacements);
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ tooling::Replacements R =
+ format::reformat(FormatStyle, FileData, Ranges, FileName);
+
+ // FIXME: Remove this copy when tooling::Replacements is implemented as a
+ // vector instead of a set.
+ std::vector<tooling::Replacement> FormattingReplacements;
+ std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
+
+ if (FormattingReplacements.empty()) {
+ FormattedFileData = FileData;
+ return true;
+ }
+
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ SM.overrideFileContents(Files.getFile(FileName),
+ llvm::MemoryBuffer::getMemBufferCopy(FileData));
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
+}
+
+int main(int argc, char **argv) {
+ cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
+
+ cl::SetVersionPrinter(&printVersion);
+ cl::ParseCommandLineOptions(argc, argv);
+
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
+
+ // Determine a formatting style from options.
+ format::FormatStyle FormatStyle;
+ if (DoFormat) {
+ auto FormatStyleOrError =
+ format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
+ if (!FormatStyleOrError) {
+ llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
+ return 1;
+ }
+ FormatStyle = *FormatStyleOrError;
+ }
+
+ TUReplacements TURs;
+ TUReplacementFiles TUFiles;
+
+ std::error_code ErrorCode =
+ collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
+
+ TUDiagnostics TUDs;
+ TUFiles.clear();
+ ErrorCode =
+ collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
+
+ if (ErrorCode) {
+ errs() << "Trouble iterating over directory '" << Directory
+ << "': " << ErrorCode.message() << "\n";
+ return 1;
+ }
+
+ // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
+ // command line option) when exiting main().
+ std::unique_ptr<ScopedFileRemover> Remover;
+ if (RemoveTUReplacementFiles)
+ Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
+
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+
+ FileToReplacementsMap GroupedReplacements;
+ if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
+ return 1;
+ if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
+ return 1;
+
+ Rewriter ReplacementsRewriter(SM, LangOptions());
+
+ for (const auto &FileAndReplacements : GroupedReplacements) {
+ // This shouldn't happen but if a file somehow has no replacements skip to
+ // next file.
+ if (FileAndReplacements.second.empty())
+ continue;
+
+ std::string NewFileData;
+ StringRef FileName = FileAndReplacements.first->getName();
+ if (!applyReplacements(FileAndReplacements.second, NewFileData,
+ Diagnostics)) {
+ errs() << "Failed to apply replacements to " << FileName << "\n";
+ continue;
+ }
+
+ // Apply formatting if requested.
+ if (DoFormat &&
+ !applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
+ FormatStyle, Diagnostics)) {
+ errs() << "Failed to apply reformatting replacements for " << FileName
+ << "\n";
+ continue;
+ }
+
+ // Write new file to disk
+ std::error_code EC;
+ llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Could not open " << FileName << " for writing\n";
+ continue;
+ }
+
+ FileStream << NewFileData;
+ }
+
+ return 0;
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_library(clangMove
+ ClangMove.cpp
+ HelperDeclRefGraph.cpp
+
+ LINK_LIBS
+ clangAnalysis
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangTooling
+ clangToolingCore
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===-- ClangMove.cpp - Implement ClangMove functationalities ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "HelperDeclRefGraph.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "clang-move"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace move {
+namespace {
+
+// FIXME: Move to ASTMatchers.
+AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
+
+AST_MATCHER(NamedDecl, notInMacro) { return !Node.getLocation().isMacroID(); }
+
+AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
+ ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
+ const auto *Context = Node.getDeclContext();
+ if (!Context)
+ return false;
+ while (const auto *NextContext = Context->getParent()) {
+ if (isa<NamespaceDecl>(NextContext) ||
+ isa<TranslationUnitDecl>(NextContext))
+ break;
+ Context = NextContext;
+ }
+ return InnerMatcher.matches(*Decl::castFromDeclContext(Context), Finder,
+ Builder);
+}
+
+AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
+ ast_matchers::internal::Matcher<CXXRecordDecl>, InnerMatcher) {
+ const CXXRecordDecl *Parent = Node.getParent();
+ if (!Parent)
+ return false;
+ while (const auto *NextParent =
+ dyn_cast<CXXRecordDecl>(Parent->getParent())) {
+ Parent = NextParent;
+ }
+
+ return InnerMatcher.matches(*Parent, Finder, Builder);
+}
+
+// Make the Path absolute using the CurrentDir if the Path is not an absolute
+// path. An empty Path will result in an empty string.
+std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
+ if (Path.empty())
+ return "";
+ llvm::SmallString<128> InitialDirectory(CurrentDir);
+ llvm::SmallString<128> AbsolutePath(Path);
+ if (std::error_code EC =
+ llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath))
+ llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
+ << '\n';
+ llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
+ llvm::sys::path::native(AbsolutePath);
+ return AbsolutePath.str();
+}
+
+// Make the Path absolute using the current working directory of the given
+// SourceManager if the Path is not an absolute path.
+//
+// The Path can be a path relative to the build directory, or retrieved from
+// the SourceManager.
+std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
+ llvm::SmallString<128> AbsolutePath(Path);
+ if (std::error_code EC =
+ SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
+ AbsolutePath))
+ llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
+ << '\n';
+ // Handle symbolic link path cases.
+ // We are trying to get the real file path of the symlink.
+ const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
+ llvm::sys::path::parent_path(AbsolutePath.str()));
+ if (Dir) {
+ StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
+ SmallVector<char, 128> AbsoluteFilename;
+ llvm::sys::path::append(AbsoluteFilename, DirName,
+ llvm::sys::path::filename(AbsolutePath.str()));
+ return llvm::StringRef(AbsoluteFilename.data(), AbsoluteFilename.size())
+ .str();
+ }
+ return AbsolutePath.str();
+}
+
+// Matches AST nodes that are expanded within the given AbsoluteFilePath.
+AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
+ std::string, AbsoluteFilePath) {
+ auto &SourceManager = Finder->getASTContext().getSourceManager();
+ auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart());
+ if (ExpansionLoc.isInvalid())
+ return false;
+ auto FileEntry =
+ SourceManager.getFileEntryForID(SourceManager.getFileID(ExpansionLoc));
+ if (!FileEntry)
+ return false;
+ return MakeAbsolutePath(SourceManager, FileEntry->getName()) ==
+ AbsoluteFilePath;
+}
+
+class FindAllIncludes : public clang::PPCallbacks {
+public:
+ explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool)
+ : SM(*SM), MoveTool(MoveTool) {}
+
+ void InclusionDirective(clang::SourceLocation HashLoc,
+ const clang::Token & /*IncludeTok*/,
+ StringRef FileName, bool IsAngled,
+ clang::CharSourceRange FilenameRange,
+ const clang::FileEntry * /*File*/,
+ StringRef SearchPath, StringRef /*RelativePath*/,
+ const clang::Module * /*Imported*/) override {
+ if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
+ MoveTool->addIncludes(FileName, IsAngled, SearchPath,
+ FileEntry->getName(), FilenameRange, SM);
+ }
+
+private:
+ const SourceManager &SM;
+ ClangMoveTool *const MoveTool;
+};
+
+/// Add a declatration being moved to new.h/cc. Note that the declaration will
+/// also be deleted in old.h/cc.
+void MoveDeclFromOldFileToNewFile(ClangMoveTool *MoveTool, const NamedDecl *D) {
+ MoveTool->getMovedDecls().push_back(D);
+ MoveTool->addRemovedDecl(D);
+ MoveTool->getUnremovedDeclsInOldHeader().erase(D);
+}
+
+class FunctionDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+ explicit FunctionDeclarationMatch(ClangMoveTool *MoveTool)
+ : MoveTool(MoveTool) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *FD = Result.Nodes.getNodeAs<clang::FunctionDecl>("function");
+ assert(FD);
+ const clang::NamedDecl *D = FD;
+ if (const auto *FTD = FD->getDescribedFunctionTemplate())
+ D = FTD;
+ MoveDeclFromOldFileToNewFile(MoveTool, D);
+ }
+
+private:
+ ClangMoveTool *MoveTool;
+};
+
+class VarDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+ explicit VarDeclarationMatch(ClangMoveTool *MoveTool)
+ : MoveTool(MoveTool) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>("var");
+ assert(VD);
+ MoveDeclFromOldFileToNewFile(MoveTool, VD);
+ }
+
+private:
+ ClangMoveTool *MoveTool;
+};
+
+class TypeAliasMatch : public MatchFinder::MatchCallback {
+public:
+ explicit TypeAliasMatch(ClangMoveTool *MoveTool)
+ : MoveTool(MoveTool) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ if (const auto *TD = Result.Nodes.getNodeAs<clang::TypedefDecl>("typedef"))
+ MoveDeclFromOldFileToNewFile(MoveTool, TD);
+ else if (const auto *TAD =
+ Result.Nodes.getNodeAs<clang::TypeAliasDecl>("type_alias")) {
+ const NamedDecl * D = TAD;
+ if (const auto * TD = TAD->getDescribedAliasTemplate())
+ D = TD;
+ MoveDeclFromOldFileToNewFile(MoveTool, D);
+ }
+ }
+
+private:
+ ClangMoveTool *MoveTool;
+};
+
+class EnumDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+ explicit EnumDeclarationMatch(ClangMoveTool *MoveTool)
+ : MoveTool(MoveTool) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *ED = Result.Nodes.getNodeAs<clang::EnumDecl>("enum");
+ assert(ED);
+ MoveDeclFromOldFileToNewFile(MoveTool, ED);
+ }
+
+private:
+ ClangMoveTool *MoveTool;
+};
+
+class ClassDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+ explicit ClassDeclarationMatch(ClangMoveTool *MoveTool)
+ : MoveTool(MoveTool) {}
+ void run(const MatchFinder::MatchResult &Result) override {
+ clang::SourceManager* SM = &Result.Context->getSourceManager();
+ if (const auto *CMD =
+ Result.Nodes.getNodeAs<clang::CXXMethodDecl>("class_method"))
+ MatchClassMethod(CMD, SM);
+ else if (const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>(
+ "class_static_var_decl"))
+ MatchClassStaticVariable(VD, SM);
+ else if (const auto *CD = Result.Nodes.getNodeAs<clang::CXXRecordDecl>(
+ "moved_class"))
+ MatchClassDeclaration(CD, SM);
+ }
+
+private:
+ void MatchClassMethod(const clang::CXXMethodDecl* CMD,
+ clang::SourceManager* SM) {
+ // Skip inline class methods. isInline() ast matcher doesn't ignore this
+ // case.
+ if (!CMD->isInlined()) {
+ MoveTool->getMovedDecls().push_back(CMD);
+ MoveTool->addRemovedDecl(CMD);
+ // Get template class method from its method declaration as
+ // UnremovedDecls stores template class method.
+ if (const auto *FTD = CMD->getDescribedFunctionTemplate())
+ MoveTool->getUnremovedDeclsInOldHeader().erase(FTD);
+ else
+ MoveTool->getUnremovedDeclsInOldHeader().erase(CMD);
+ }
+ }
+
+ void MatchClassStaticVariable(const clang::NamedDecl *VD,
+ clang::SourceManager* SM) {
+ MoveDeclFromOldFileToNewFile(MoveTool, VD);
+ }
+
+ void MatchClassDeclaration(const clang::CXXRecordDecl *CD,
+ clang::SourceManager* SM) {
+ // Get class template from its class declaration as UnremovedDecls stores
+ // class template.
+ if (const auto *TC = CD->getDescribedClassTemplate())
+ MoveTool->getMovedDecls().push_back(TC);
+ else
+ MoveTool->getMovedDecls().push_back(CD);
+ MoveTool->addRemovedDecl(MoveTool->getMovedDecls().back());
+ MoveTool->getUnremovedDeclsInOldHeader().erase(
+ MoveTool->getMovedDecls().back());
+ }
+
+ ClangMoveTool *MoveTool;
+};
+
+// Expand to get the end location of the line where the EndLoc of the given
+// Decl.
+SourceLocation
+getLocForEndOfDecl(const clang::Decl *D,
+ const LangOptions &LangOpts = clang::LangOptions()) {
+ const auto &SM = D->getASTContext().getSourceManager();
+ auto EndExpansionLoc = SM.getExpansionLoc(D->getLocEnd());
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
+ // Try to load the file buffer.
+ bool InvalidTemp = false;
+ llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+ if (InvalidTemp)
+ return SourceLocation();
+
+ const char *TokBegin = File.data() + LocInfo.second;
+ // Lex from the start of the given location.
+ Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
+ TokBegin, File.end());
+
+ llvm::SmallVector<char, 16> Line;
+ // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+ Lex.setParsingPreprocessorDirective(true);
+ Lex.ReadToEndOfLine(&Line);
+ SourceLocation EndLoc = EndExpansionLoc.getLocWithOffset(Line.size());
+ // If we already reach EOF, just return the EOF SourceLocation;
+ // otherwise, move 1 offset ahead to include the trailing newline character
+ // '\n'.
+ return SM.getLocForEndOfFile(LocInfo.first) == EndLoc
+ ? EndLoc
+ : EndLoc.getLocWithOffset(1);
+}
+
+// Get full range of a Decl including the comments associated with it.
+clang::CharSourceRange
+getFullRange(const clang::Decl *D,
+ const clang::LangOptions &options = clang::LangOptions()) {
+ const auto &SM = D->getASTContext().getSourceManager();
+ clang::SourceRange Full(SM.getExpansionLoc(D->getLocStart()),
+ getLocForEndOfDecl(D));
+ // Expand to comments that are associated with the Decl.
+ if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+ if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getLocEnd()))
+ Full.setEnd(Comment->getLocEnd());
+ // FIXME: Don't delete a preceding comment, if there are no other entities
+ // it could refer to.
+ if (SM.isBeforeInTranslationUnit(Comment->getLocStart(), Full.getBegin()))
+ Full.setBegin(Comment->getLocStart());
+ }
+
+ return clang::CharSourceRange::getCharRange(Full);
+}
+
+std::string getDeclarationSourceText(const clang::Decl *D) {
+ const auto &SM = D->getASTContext().getSourceManager();
+ llvm::StringRef SourceText =
+ clang::Lexer::getSourceText(getFullRange(D), SM, clang::LangOptions());
+ return SourceText.str();
+}
+
+bool isInHeaderFile(const clang::Decl *D,
+ llvm::StringRef OriginalRunningDirectory,
+ llvm::StringRef OldHeader) {
+ const auto &SM = D->getASTContext().getSourceManager();
+ if (OldHeader.empty())
+ return false;
+ auto ExpansionLoc = SM.getExpansionLoc(D->getLocStart());
+ if (ExpansionLoc.isInvalid())
+ return false;
+
+ if (const auto *FE = SM.getFileEntryForID(SM.getFileID(ExpansionLoc))) {
+ return MakeAbsolutePath(SM, FE->getName()) ==
+ MakeAbsolutePath(OriginalRunningDirectory, OldHeader);
+ }
+
+ return false;
+}
+
+std::vector<std::string> getNamespaces(const clang::Decl *D) {
+ std::vector<std::string> Namespaces;
+ for (const auto *Context = D->getDeclContext(); Context;
+ Context = Context->getParent()) {
+ if (llvm::isa<clang::TranslationUnitDecl>(Context) ||
+ llvm::isa<clang::LinkageSpecDecl>(Context))
+ break;
+
+ if (const auto *ND = llvm::dyn_cast<clang::NamespaceDecl>(Context))
+ Namespaces.push_back(ND->getName().str());
+ }
+ std::reverse(Namespaces.begin(), Namespaces.end());
+ return Namespaces;
+}
+
+clang::tooling::Replacements
+createInsertedReplacements(const std::vector<std::string> &Includes,
+ const std::vector<const NamedDecl *> &Decls,
+ llvm::StringRef FileName, bool IsHeader = false,
+ StringRef OldHeaderInclude = "") {
+ std::string NewCode;
+ std::string GuardName(FileName);
+ if (IsHeader) {
+ for (size_t i = 0; i < GuardName.size(); ++i) {
+ if (!isAlphanumeric(GuardName[i]))
+ GuardName[i] = '_';
+ }
+ GuardName = StringRef(GuardName).upper();
+ NewCode += "#ifndef " + GuardName + "\n";
+ NewCode += "#define " + GuardName + "\n\n";
+ }
+
+ NewCode += OldHeaderInclude;
+ // Add #Includes.
+ for (const auto &Include : Includes)
+ NewCode += Include;
+
+ if (!Includes.empty())
+ NewCode += "\n";
+
+ // Add moved class definition and its related declarations. All declarations
+ // in same namespace are grouped together.
+ //
+ // Record namespaces where the current position is in.
+ std::vector<std::string> CurrentNamespaces;
+ for (const auto *MovedDecl : Decls) {
+ // The namespaces of the declaration being moved.
+ std::vector<std::string> DeclNamespaces = getNamespaces(MovedDecl);
+ auto CurrentIt = CurrentNamespaces.begin();
+ auto DeclIt = DeclNamespaces.begin();
+ // Skip the common prefix.
+ while (CurrentIt != CurrentNamespaces.end() &&
+ DeclIt != DeclNamespaces.end()) {
+ if (*CurrentIt != *DeclIt)
+ break;
+ ++CurrentIt;
+ ++DeclIt;
+ }
+ // Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
+ // which is used for next iteration of this loop.
+ std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
+ CurrentIt);
+ NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
+
+
+ // End with CurrentNamespace.
+ bool HasEndCurrentNamespace = false;
+ auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
+ for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
+ --RemainingSize, ++It) {
+ assert(It < CurrentNamespaces.rend());
+ NewCode += "} // namespace " + *It + "\n";
+ HasEndCurrentNamespace = true;
+ }
+ // Add trailing '\n' after the nested namespace definition.
+ if (HasEndCurrentNamespace)
+ NewCode += "\n";
+
+ // If the moved declaration is not in CurrentNamespace, add extra namespace
+ // definitions.
+ bool IsInNewNamespace = false;
+ while (DeclIt != DeclNamespaces.end()) {
+ NewCode += "namespace " + *DeclIt + " {\n";
+ IsInNewNamespace = true;
+ ++DeclIt;
+ }
+ // If the moved declaration is in same namespace CurrentNamespace, add
+ // a preceeding `\n' before the moved declaration.
+ // FIXME: Don't add empty lines between using declarations.
+ if (!IsInNewNamespace)
+ NewCode += "\n";
+ NewCode += getDeclarationSourceText(MovedDecl);
+ CurrentNamespaces = std::move(NextNamespaces);
+ }
+ std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
+ for (const auto &NS : CurrentNamespaces)
+ NewCode += "} // namespace " + NS + "\n";
+
+ if (IsHeader)
+ NewCode += "\n#endif // " + GuardName + "\n";
+ return clang::tooling::Replacements(
+ clang::tooling::Replacement(FileName, 0, 0, NewCode));
+}
+
+// Return a set of all decls which are used/referenced by the given Decls.
+// Specically, given a class member declaration, this method will return all
+// decls which are used by the whole class.
+llvm::DenseSet<const Decl *>
+getUsedDecls(const HelperDeclRefGraph *RG,
+ const std::vector<const NamedDecl *> &Decls) {
+ assert(RG);
+ llvm::DenseSet<const CallGraphNode *> Nodes;
+ for (const auto *D : Decls) {
+ auto Result = RG->getReachableNodes(
+ HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
+ Nodes.insert(Result.begin(), Result.end());
+ }
+ llvm::DenseSet<const Decl *> Results;
+ for (const auto *Node : Nodes)
+ Results.insert(Node->getDecl());
+ return Results;
+}
+
+} // namespace
+
+std::unique_ptr<clang::ASTConsumer>
+ClangMoveAction::CreateASTConsumer(clang::CompilerInstance &Compiler,
+ StringRef /*InFile*/) {
+ Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllIncludes>(
+ &Compiler.getSourceManager(), &MoveTool));
+ return MatchFinder.newASTConsumer();
+}
+
+ClangMoveTool::ClangMoveTool(ClangMoveContext *const Context,
+ DeclarationReporter *const Reporter)
+ : Context(Context), Reporter(Reporter) {
+ if (!Context->Spec.NewHeader.empty())
+ CCIncludes.push_back("#include \"" + Context->Spec.NewHeader + "\"\n");
+}
+
+void ClangMoveTool::addRemovedDecl(const NamedDecl *Decl) {
+ const auto &SM = Decl->getASTContext().getSourceManager();
+ auto Loc = Decl->getLocation();
+ StringRef FilePath = SM.getFilename(Loc);
+ FilePathToFileID[FilePath] = SM.getFileID(Loc);
+ RemovedDecls.push_back(Decl);
+}
+
+void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
+ auto InOldHeader =
+ isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
+ auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
+ auto InOldFiles = anyOf(InOldHeader, InOldCC);
+ auto classTemplateForwardDecls =
+ classTemplateDecl(unless(has(cxxRecordDecl(isDefinition()))));
+ auto ForwardClassDecls = namedDecl(
+ anyOf(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition()))),
+ classTemplateForwardDecls));
+ auto TopLevelDecl =
+ hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
+
+ //============================================================================
+ // Matchers for old header
+ //============================================================================
+ // Match all top-level named declarations (e.g. function, variable, enum) in
+ // old header, exclude forward class declarations and namespace declarations.
+ //
+ // We consider declarations inside a class belongs to the class. So these
+ // declarations will be ignored.
+ auto AllDeclsInHeader = namedDecl(
+ unless(ForwardClassDecls), unless(namespaceDecl()),
+ unless(usingDirectiveDecl()), // using namespace decl.
+ InOldHeader,
+ hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
+ hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
+ Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
+
+ // Don't register other matchers when dumping all declarations in header.
+ if (Context->DumpDeclarations)
+ return;
+
+ // Match forward declarations in old header.
+ Finder->addMatcher(namedDecl(ForwardClassDecls, InOldHeader).bind("fwd_decl"),
+ this);
+
+ //============================================================================
+ // Matchers for old cc
+ //============================================================================
+ auto IsOldCCTopLevelDecl = allOf(
+ hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC);
+ // Matching using decls/type alias decls which are in named/anonymous/global
+ // namespace, these decls are always copied to new.h/cc. Those in classes,
+ // functions are covered in other matchers.
+ Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
+ usingDirectiveDecl(IsOldCCTopLevelDecl),
+ typeAliasDecl(IsOldCCTopLevelDecl)),
+ notInMacro())
+ .bind("using_decl"),
+ this);
+
+ // Match static functions/variable definitions which are defined in named
+ // namespaces.
+ Optional<ast_matchers::internal::Matcher<NamedDecl>> HasAnySymbolNames;
+ for (StringRef SymbolName : Context->Spec.Names) {
+ llvm::StringRef GlobalSymbolName = SymbolName.trim().ltrim(':');
+ const auto HasName = hasName(("::" + GlobalSymbolName).str());
+ HasAnySymbolNames =
+ HasAnySymbolNames ? anyOf(*HasAnySymbolNames, HasName) : HasName;
+ }
+
+ if (!HasAnySymbolNames) {
+ llvm::errs() << "No symbols being moved.\n";
+ return;
+ }
+ auto InMovedClass =
+ hasOutermostEnclosingClass(cxxRecordDecl(*HasAnySymbolNames));
+
+ // Matchers for helper declarations in old.cc.
+ auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
+ auto NotInMovedClass= allOf(unless(InMovedClass), InOldCC);
+ auto IsOldCCHelper =
+ allOf(NotInMovedClass, anyOf(isStaticStorageClass(), InAnonymousNS));
+ // Match helper classes separately with helper functions/variables since we
+ // want to reuse these matchers in finding helpers usage below.
+ //
+ // There could be forward declarations usage for helpers, especially for
+ // classes and functions. We need include these forward declarations.
+ //
+ // Forward declarations for variable helpers will be excluded as these
+ // declarations (with "extern") are not supposed in cpp file.
+ auto HelperFuncOrVar =
+ namedDecl(notInMacro(), anyOf(functionDecl(IsOldCCHelper),
+ varDecl(isDefinition(), IsOldCCHelper)));
+ auto HelperClasses =
+ cxxRecordDecl(notInMacro(), NotInMovedClass, InAnonymousNS);
+ // Save all helper declarations in old.cc.
+ Finder->addMatcher(
+ namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
+ this);
+
+ // Construct an AST-based call graph of helper declarations in old.cc.
+ // In the following matcheres, "dc" is a caller while "helper_decls" and
+ // "used_class" is a callee, so a new edge starting from caller to callee will
+ // be add in the graph.
+ //
+ // Find helper function/variable usages.
+ Finder->addMatcher(
+ declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc")))
+ .bind("func_ref"),
+ &RGBuilder);
+ // Find helper class usages.
+ Finder->addMatcher(
+ typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))),
+ hasAncestor(decl().bind("dc"))),
+ &RGBuilder);
+
+ //============================================================================
+ // Matchers for old files, including old.h/old.cc
+ //============================================================================
+ // Create a MatchCallback for class declarations.
+ MatchCallbacks.push_back(llvm::make_unique<ClassDeclarationMatch>(this));
+ // Match moved class declarations.
+ auto MovedClass = cxxRecordDecl(InOldFiles, *HasAnySymbolNames,
+ isDefinition(), TopLevelDecl)
+ .bind("moved_class");
+ Finder->addMatcher(MovedClass, MatchCallbacks.back().get());
+ // Match moved class methods (static methods included) which are defined
+ // outside moved class declaration.
+ Finder->addMatcher(
+ cxxMethodDecl(InOldFiles, ofOutermostEnclosingClass(*HasAnySymbolNames),
+ isDefinition())
+ .bind("class_method"),
+ MatchCallbacks.back().get());
+ // Match static member variable definition of the moved class.
+ Finder->addMatcher(
+ varDecl(InMovedClass, InOldFiles, isDefinition(), isStaticDataMember())
+ .bind("class_static_var_decl"),
+ MatchCallbacks.back().get());
+
+ MatchCallbacks.push_back(llvm::make_unique<FunctionDeclarationMatch>(this));
+ Finder->addMatcher(functionDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl)
+ .bind("function"),
+ MatchCallbacks.back().get());
+
+ MatchCallbacks.push_back(llvm::make_unique<VarDeclarationMatch>(this));
+ Finder->addMatcher(
+ varDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl).bind("var"),
+ MatchCallbacks.back().get());
+
+ // Match enum definition in old.h. Enum helpers (which are defined in old.cc)
+ // will not be moved for now no matter whether they are used or not.
+ MatchCallbacks.push_back(llvm::make_unique<EnumDeclarationMatch>(this));
+ Finder->addMatcher(
+ enumDecl(InOldHeader, *HasAnySymbolNames, isDefinition(), TopLevelDecl)
+ .bind("enum"),
+ MatchCallbacks.back().get());
+
+ // Match type alias in old.h, this includes "typedef" and "using" type alias
+ // declarations. Type alias helpers (which are defined in old.cc) will not be
+ // moved for now no matter whether they are used or not.
+ MatchCallbacks.push_back(llvm::make_unique<TypeAliasMatch>(this));
+ Finder->addMatcher(namedDecl(anyOf(typedefDecl().bind("typedef"),
+ typeAliasDecl().bind("type_alias")),
+ InOldHeader, *HasAnySymbolNames, TopLevelDecl),
+ MatchCallbacks.back().get());
+}
+
+void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const auto *D =
+ Result.Nodes.getNodeAs<clang::NamedDecl>("decls_in_header")) {
+ UnremovedDeclsInOldHeader.insert(D);
+ } else if (const auto *FWD =
+ Result.Nodes.getNodeAs<clang::CXXRecordDecl>("fwd_decl")) {
+ // Skip all forward declarations which appear after moved class declaration.
+ if (RemovedDecls.empty()) {
+ if (const auto *DCT = FWD->getDescribedClassTemplate())
+ MovedDecls.push_back(DCT);
+ else
+ MovedDecls.push_back(FWD);
+ }
+ } else if (const auto *ND =
+ Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
+ MovedDecls.push_back(ND);
+ HelperDeclarations.push_back(ND);
+ DEBUG(llvm::dbgs() << "Add helper : "
+ << ND->getNameAsString() << " (" << ND << ")\n");
+ } else if (const auto *UD =
+ Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
+ MovedDecls.push_back(UD);
+ }
+}
+
+std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
+ return MakeAbsolutePath(Context->OriginalRunningDirectory, Path);
+}
+
+void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
+ llvm::StringRef SearchPath,
+ llvm::StringRef FileName,
+ clang::CharSourceRange IncludeFilenameRange,
+ const SourceManager &SM) {
+ SmallVector<char, 128> HeaderWithSearchPath;
+ llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
+ std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
+ if (AbsoluteOldHeader ==
+ MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
+ HeaderWithSearchPath.size()))) {
+ OldHeaderIncludeRange = IncludeFilenameRange;
+ return;
+ }
+
+ std::string IncludeLine =
+ IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
+ : ("#include \"" + IncludeHeader + "\"\n").str();
+
+ std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
+ if (AbsoluteOldHeader == AbsoluteCurrentFile) {
+ HeaderIncludes.push_back(IncludeLine);
+ } else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
+ CCIncludes.push_back(IncludeLine);
+ }
+}
+
+void ClangMoveTool::removeDeclsInOldFiles() {
+ if (RemovedDecls.empty()) return;
+
+ // If old_header is not specified (only move declarations from old.cc), remain
+ // all the helper function declarations in old.cc as UnremovedDeclsInOldHeader
+ // is empty in this case, there is no way to verify unused/used helpers.
+ if (!Context->Spec.OldHeader.empty()) {
+ std::vector<const NamedDecl *> UnremovedDecls;
+ for (const auto *D : UnremovedDeclsInOldHeader)
+ UnremovedDecls.push_back(D);
+
+ auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls);
+
+ // We remove the helper declarations which are not used in the old.cc after
+ // moving the given declarations.
+ for (const auto *D : HelperDeclarations) {
+ DEBUG(llvm::dbgs() << "Check helper is used: "
+ << D->getNameAsString() << " (" << D << ")\n");
+ if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
+ D->getCanonicalDecl()))) {
+ DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
+ << D->getNameAsString() << " (" << D << ")\n");
+ RemovedDecls.push_back(D);
+ }
+ }
+ }
+
+ for (const auto *RemovedDecl : RemovedDecls) {
+ const auto &SM = RemovedDecl->getASTContext().getSourceManager();
+ auto Range = getFullRange(RemovedDecl);
+ clang::tooling::Replacement RemoveReplacement(
+ SM,
+ clang::CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()),
+ "");
+ std::string FilePath = RemoveReplacement.getFilePath().str();
+ auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement);
+ if (Err)
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ }
+ const auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
+
+ // Post process of cleanup around all the replacements.
+ for (auto &FileAndReplacements : Context->FileToReplacements) {
+ StringRef FilePath = FileAndReplacements.first;
+ // Add #include of new header to old header.
+ if (Context->Spec.OldDependOnNew &&
+ MakeAbsolutePath(SM, FilePath) ==
+ makeAbsolutePath(Context->Spec.OldHeader)) {
+ // FIXME: Minimize the include path like include-fixer.
+ std::string IncludeNewH =
+ "#include \"" + Context->Spec.NewHeader + "\"\n";
+ // This replacment for inserting header will be cleaned up at the end.
+ auto Err = FileAndReplacements.second.add(
+ tooling::Replacement(FilePath, UINT_MAX, 0, IncludeNewH));
+ if (Err)
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ }
+
+ auto SI = FilePathToFileID.find(FilePath);
+ // Ignore replacements for new.h/cc.
+ if (SI == FilePathToFileID.end()) continue;
+ llvm::StringRef Code = SM.getBufferData(SI->second);
+ auto Style = format::getStyle("file", FilePath, Context->FallbackStyle);
+ if (!Style) {
+ llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+ continue;
+ }
+ auto CleanReplacements = format::cleanupAroundReplacements(
+ Code, Context->FileToReplacements[FilePath], *Style);
+
+ if (!CleanReplacements) {
+ llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+ continue;
+ }
+ Context->FileToReplacements[FilePath] = *CleanReplacements;
+ }
+}
+
+void ClangMoveTool::moveDeclsToNewFiles() {
+ std::vector<const NamedDecl *> NewHeaderDecls;
+ std::vector<const NamedDecl *> NewCCDecls;
+ for (const auto *MovedDecl : MovedDecls) {
+ if (isInHeaderFile(MovedDecl, Context->OriginalRunningDirectory,
+ Context->Spec.OldHeader))
+ NewHeaderDecls.push_back(MovedDecl);
+ else
+ NewCCDecls.push_back(MovedDecl);
+ }
+
+ auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
+ std::vector<const NamedDecl *> ActualNewCCDecls;
+
+ // Filter out all unused helpers in NewCCDecls.
+ // We only move the used helpers (including transively used helpers) and the
+ // given symbols being moved.
+ for (const auto *D : NewCCDecls) {
+ if (llvm::is_contained(HelperDeclarations, D) &&
+ !UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
+ D->getCanonicalDecl())))
+ continue;
+
+ DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
+ << " " << D << "\n");
+ ActualNewCCDecls.push_back(D);
+ }
+
+ if (!Context->Spec.NewHeader.empty()) {
+ std::string OldHeaderInclude =
+ Context->Spec.NewDependOnOld
+ ? "#include \"" + Context->Spec.OldHeader + "\"\n"
+ : "";
+ Context->FileToReplacements[Context->Spec.NewHeader] =
+ createInsertedReplacements(HeaderIncludes, NewHeaderDecls,
+ Context->Spec.NewHeader, /*IsHeader=*/true,
+ OldHeaderInclude);
+ }
+ if (!Context->Spec.NewCC.empty())
+ Context->FileToReplacements[Context->Spec.NewCC] =
+ createInsertedReplacements(CCIncludes, ActualNewCCDecls,
+ Context->Spec.NewCC);
+}
+
+// Move all contents from OldFile to NewFile.
+void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
+ StringRef NewFile) {
+ const FileEntry *FE = SM.getFileManager().getFile(makeAbsolutePath(OldFile));
+ if (!FE) {
+ llvm::errs() << "Failed to get file: " << OldFile << "\n";
+ return;
+ }
+ FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User);
+ auto Begin = SM.getLocForStartOfFile(ID);
+ auto End = SM.getLocForEndOfFile(ID);
+ clang::tooling::Replacement RemoveAll (
+ SM, clang::CharSourceRange::getCharRange(Begin, End), "");
+ std::string FilePath = RemoveAll.getFilePath().str();
+ Context->FileToReplacements[FilePath] =
+ clang::tooling::Replacements(RemoveAll);
+
+ StringRef Code = SM.getBufferData(ID);
+ if (!NewFile.empty()) {
+ auto AllCode = clang::tooling::Replacements(
+ clang::tooling::Replacement(NewFile, 0, 0, Code));
+ // If we are moving from old.cc, an extra step is required: excluding
+ // the #include of "old.h", instead, we replace it with #include of "new.h".
+ if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) {
+ AllCode = AllCode.merge(
+ clang::tooling::Replacements(clang::tooling::Replacement(
+ SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"')));
+ }
+ Context->FileToReplacements[NewFile] = std::move(AllCode);
+ }
+}
+
+void ClangMoveTool::onEndOfTranslationUnit() {
+ if (Context->DumpDeclarations) {
+ assert(Reporter);
+ for (const auto *Decl : UnremovedDeclsInOldHeader) {
+ auto Kind = Decl->getKind();
+ const std::string QualifiedName = Decl->getQualifiedNameAsString();
+ if (Kind == Decl::Kind::Var)
+ Reporter->reportDeclaration(QualifiedName, "Variable");
+ else if (Kind == Decl::Kind::Function ||
+ Kind == Decl::Kind::FunctionTemplate)
+ Reporter->reportDeclaration(QualifiedName, "Function");
+ else if (Kind == Decl::Kind::ClassTemplate ||
+ Kind == Decl::Kind::CXXRecord)
+ Reporter->reportDeclaration(QualifiedName, "Class");
+ else if (Kind == Decl::Kind::Enum)
+ Reporter->reportDeclaration(QualifiedName, "Enum");
+ else if (Kind == Decl::Kind::Typedef ||
+ Kind == Decl::Kind::TypeAlias ||
+ Kind == Decl::Kind::TypeAliasTemplate)
+ Reporter->reportDeclaration(QualifiedName, "TypeAlias");
+ }
+ return;
+ }
+
+ if (RemovedDecls.empty())
+ return;
+ // Ignore symbols that are not supported (e.g. typedef and enum) when
+ // checking if there is unremoved symbol in old header. This makes sure that
+ // we always move old files to new files when all symbols produced from
+ // dump_decls are moved.
+ auto IsSupportedKind = [](const clang::NamedDecl *Decl) {
+ switch (Decl->getKind()) {
+ case Decl::Kind::Function:
+ case Decl::Kind::FunctionTemplate:
+ case Decl::Kind::ClassTemplate:
+ case Decl::Kind::CXXRecord:
+ case Decl::Kind::Enum:
+ case Decl::Kind::Typedef:
+ case Decl::Kind::TypeAlias:
+ case Decl::Kind::TypeAliasTemplate:
+ case Decl::Kind::Var:
+ return true;
+ default:
+ return false;
+ }
+ };
+ if (std::none_of(UnremovedDeclsInOldHeader.begin(),
+ UnremovedDeclsInOldHeader.end(), IsSupportedKind) &&
+ !Context->Spec.OldHeader.empty()) {
+ auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
+ moveAll(SM, Context->Spec.OldHeader, Context->Spec.NewHeader);
+ moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
+ return;
+ }
+ DEBUG(RGBuilder.getGraph()->dump());
+ moveDeclsToNewFiles();
+ removeDeclsInOldFiles();
+}
+
+} // namespace move
+} // namespace clang
--- /dev/null
+//===-- ClangMove.h - Clang move -----------------------------------------===//
+//
+// 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_MOVE_CLANGMOVE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
+
+#include "HelperDeclRefGraph.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringMap.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace move {
+
+// A reporter which collects and reports declarations in old header.
+class DeclarationReporter {
+public:
+ DeclarationReporter() = default;
+ ~DeclarationReporter() = default;
+
+ void reportDeclaration(llvm::StringRef DeclarationName,
+ llvm::StringRef Type) {
+ DeclarationList.emplace_back(DeclarationName, Type);
+ };
+
+ // A <DeclarationName, DeclarationKind> pair.
+ // The DeclarationName is a fully qualified name for the declaration, like
+ // A::B::Foo. The DeclarationKind is a string represents the kind of the
+ // declaration, currently only "Function" and "Class" are supported.
+ typedef std::pair<std::string, std::string> DeclarationPair;
+
+ const std::vector<DeclarationPair> getDeclarationList() const {
+ return DeclarationList;
+ }
+
+private:
+ std::vector<DeclarationPair> DeclarationList;
+};
+
+// Specify declarations being moved. It contains all information of the moved
+// declarations.
+struct MoveDefinitionSpec {
+ // The list of fully qualified names, e.g. Foo, a::Foo, b::Foo.
+ SmallVector<std::string, 4> Names;
+ // The file path of old header, can be relative path and absolute path.
+ std::string OldHeader;
+ // The file path of old cc, can be relative path and absolute path.
+ std::string OldCC;
+ // The file path of new header, can be relative path and absolute path.
+ std::string NewHeader;
+ // The file path of new cc, can be relative path and absolute path.
+ std::string NewCC;
+ // Whether old.h depends on new.h. If true, #include "new.h" will be added
+ // in old.h.
+ bool OldDependOnNew = false;
+ // Whether new.h depends on old.h. If true, #include "old.h" will be added
+ // in new.h.
+ bool NewDependOnOld = false;
+};
+
+// A Context which contains extra options which are used in ClangMoveTool.
+struct ClangMoveContext {
+ MoveDefinitionSpec Spec;
+ // The Key is file path, value is the replacements being applied to the file.
+ std::map<std::string, tooling::Replacements> &FileToReplacements;
+ // The original working directory where the local clang-move binary runs.
+ //
+ // clang-move will change its current working directory to the build
+ // directory when analyzing the source file. We save the original working
+ // directory in order to get the absolute file path for the fields in Spec.
+ std::string OriginalRunningDirectory;
+ // The name of a predefined code style.
+ std::string FallbackStyle;
+ // Whether dump all declarations in old header.
+ bool DumpDeclarations;
+};
+
+// This tool is used to move class/function definitions from the given source
+// files (old.h/cc) to new files (new.h/cc).
+// The goal of this tool is to make the new/old files as compilable as possible.
+//
+// When moving a symbol,all used helper declarations (e.g. static
+// functions/variables definitions in global/named namespace,
+// functions/variables/classes definitions in anonymous namespace) used by the
+// moved symbol in old.cc are moved to the new.cc. In addition, all
+// using-declarations in old.cc are also moved to new.cc; forward class
+// declarations in old.h are also moved to new.h.
+//
+// The remaining helper declarations which are unused by non-moved symbols in
+// old.cc will be removed.
+//
+// Note: When all declarations in old header are being moved, all code in
+// old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols
+// that are not supported (e.g. typedef and enum) so that we always move old
+// files to new files when all symbols produced from dump_decls are moved.
+class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ ClangMoveTool(ClangMoveContext *const Context,
+ DeclarationReporter *const Reporter);
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder);
+
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+ void onEndOfTranslationUnit() override;
+
+ /// Add #includes from old.h/cc files.
+ ///
+ /// \param IncludeHeader The name of the file being included, as written in
+ /// the source code.
+ /// \param IsAngled Whether the file name was enclosed in angle brackets.
+ /// \param SearchPath The search path which was used to find the IncludeHeader
+ /// in the file system. It can be a relative path or an absolute path.
+ /// \param FileName The name of file where the IncludeHeader comes from.
+ /// \param IncludeFilenameRange The source range for the written file name in
+ /// #include (i.e. "old.h" for #include "old.h") in old.cc.
+ /// \param SM The SourceManager.
+ void addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
+ llvm::StringRef SearchPath, llvm::StringRef FileName,
+ clang::CharSourceRange IncludeFilenameRange,
+ const SourceManager &SM);
+
+ std::vector<const NamedDecl *> &getMovedDecls() { return MovedDecls; }
+
+ /// Add declarations being removed from old.h/cc. For each declarations, the
+ /// method also records the mapping relationship between the corresponding
+ /// FilePath and its FileID.
+ void addRemovedDecl(const NamedDecl *Decl);
+
+ llvm::SmallPtrSet<const NamedDecl *, 8> &getUnremovedDeclsInOldHeader() {
+ return UnremovedDeclsInOldHeader;
+ }
+
+private:
+ // Make the Path absolute using the OrignalRunningDirectory if the Path is not
+ // an absolute path. An empty Path will result in an empty string.
+ std::string makeAbsolutePath(StringRef Path);
+
+ void removeDeclsInOldFiles();
+ void moveDeclsToNewFiles();
+ void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);
+
+ // Stores all MatchCallbacks created by this tool.
+ std::vector<std::unique_ptr<ast_matchers::MatchFinder::MatchCallback>>
+ MatchCallbacks;
+ // Store all potential declarations (decls being moved, forward decls) that
+ // might need to move to new.h/cc. It includes all helper declarations
+ // (include unused ones) by default. The unused ones will be filtered out in
+ // the last stage. Saving in an AST-visited order.
+ std::vector<const NamedDecl *> MovedDecls;
+ // The declarations that needs to be removed in old.cc/h.
+ std::vector<const NamedDecl *> RemovedDecls;
+ // The #includes in old_header.h.
+ std::vector<std::string> HeaderIncludes;
+ // The #includes in old_cc.cc.
+ std::vector<std::string> CCIncludes;
+ // Records all helper declarations (function/variable/class definitions in
+ // anonymous namespaces, static function/variable definitions in global/named
+ // namespaces) in old.cc. saving in an AST-visited order.
+ std::vector<const NamedDecl *> HelperDeclarations;
+ // The unmoved named declarations in old header.
+ llvm::SmallPtrSet<const NamedDecl*, 8> UnremovedDeclsInOldHeader;
+ /// The source range for the written file name in #include (i.e. "old.h" for
+ /// #include "old.h") in old.cc, including the enclosing quotes or angle
+ /// brackets.
+ clang::CharSourceRange OldHeaderIncludeRange;
+ /// Mapping from FilePath to FileID, which can be used in post processes like
+ /// cleanup around replacements.
+ llvm::StringMap<FileID> FilePathToFileID;
+ /// A context contains all running options. It is not owned.
+ ClangMoveContext *const Context;
+ /// A reporter to report all declarations from old header. It is not owned.
+ DeclarationReporter *const Reporter;
+ /// Builder for helper declarations reference graph.
+ HelperDeclRGBuilder RGBuilder;
+};
+
+class ClangMoveAction : public clang::ASTFrontendAction {
+public:
+ ClangMoveAction(ClangMoveContext *const Context,
+ DeclarationReporter *const Reporter)
+ : MoveTool(Context, Reporter) {
+ MoveTool.registerMatchers(&MatchFinder);
+ }
+
+ ~ClangMoveAction() override = default;
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ llvm::StringRef InFile) override;
+
+private:
+ ast_matchers::MatchFinder MatchFinder;
+ ClangMoveTool MoveTool;
+};
+
+class ClangMoveActionFactory : public tooling::FrontendActionFactory {
+public:
+ ClangMoveActionFactory(ClangMoveContext *const Context,
+ DeclarationReporter *const Reporter = nullptr)
+ : Context(Context), Reporter(Reporter) {}
+
+ clang::FrontendAction *create() override {
+ return new ClangMoveAction(Context, Reporter);
+ }
+
+private:
+ // Not owned.
+ ClangMoveContext *const Context;
+ DeclarationReporter *const Reporter;
+};
+
+} // namespace move
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
--- /dev/null
+//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HelperDeclRefGraph.h"
+#include "ClangMove.h"
+#include "clang/AST/Decl.h"
+#include "llvm/Support/Debug.h"
+#include <vector>
+
+#define DEBUG_TYPE "clang-move"
+
+namespace clang {
+namespace move {
+
+void HelperDeclRefGraph::print(raw_ostream &OS) const {
+ OS << " --- Call graph Dump --- \n";
+ for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
+ const CallGraphNode *N = (I->second).get();
+
+ OS << " Declarations: ";
+ N->print(OS);
+ OS << " (" << N << ") ";
+ OS << " calls: ";
+ for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
+ (*CI)->print(OS);
+ OS << " (" << CI << ") ";
+ }
+ OS << '\n';
+ }
+ OS.flush();
+}
+
+void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
+ assert(Caller);
+ assert(Callee);
+
+ // Ignore the case where Caller equals Callee. This happens in the static
+ // class member definitions in global namespace like "int CLASS::static_var =
+ // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
+ // CXXRecordDecl.
+ if (Caller == Callee) return;
+
+ // Allocate a new node, mark it as root, and process it's calls.
+ CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
+ CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
+ CallerNode->addCallee(CalleeNode);
+}
+
+void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
+
+CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
+ F = F->getCanonicalDecl();
+ std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
+ if (Node)
+ return Node.get();
+
+ Node = llvm::make_unique<CallGraphNode>(F);
+ return Node.get();
+}
+
+CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
+ auto I = DeclMap.find(D->getCanonicalDecl());
+ return I == DeclMap.end() ? nullptr : I->second.get();
+}
+
+llvm::DenseSet<const CallGraphNode *>
+HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
+ const auto *RootNode = getNode(Root);
+ if (!RootNode)
+ return {};
+ llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
+ std::function<void(const CallGraphNode *)> VisitNode =
+ [&](const CallGraphNode *Node) {
+ if (ConnectedNodes.count(Node))
+ return;
+ ConnectedNodes.insert(Node);
+ for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
+ VisitNode(*It);
+ };
+
+ VisitNode(RootNode);
+ return ConnectedNodes;
+}
+
+const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
+ const auto *DC = D->getDeclContext();
+ const auto *Result = D;
+ while (DC) {
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
+ Result = RD;
+ else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
+ Result = FD;
+ DC = DC->getParent();
+ }
+ return Result;
+}
+
+void HelperDeclRGBuilder::run(
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ // Construct the graph by adding a directed edge from caller to callee.
+ //
+ // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
+ // might be not the targetted Caller Decl, we always use the outmost enclosing
+ // FunctionDecl/CXXRecordDecl of "dc". For example,
+ //
+ // int MoveClass::F() { int a = helper(); return a; }
+ //
+ // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
+ // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
+ if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
+ const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(DC);
+ DEBUG(llvm::dbgs() << "Find helper function usage: "
+ << FuncRef->getDecl()->getNameAsString() << " ("
+ << FuncRef->getDecl() << ")\n");
+ RG->addEdge(
+ getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
+ getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
+ } else if (const auto *UsedClass =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
+ const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+ assert(DC);
+ DEBUG(llvm::dbgs() << "Find helper class usage: "
+ << UsedClass->getNameAsString() << " (" << UsedClass
+ << ")\n");
+ RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
+ }
+}
+
+} // namespace move
+} // namespace clang
--- /dev/null
+//===-- UsedHelperDeclFinder.h - AST-based call graph for helper decls ----===//
+//
+// 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_MOVE_USED_HELPER_DECL_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/DenseSet.h"
+#include <memory>
+#include <vector>
+
+namespace clang {
+namespace move {
+
+// A reference graph for finding used/unused helper declarations in a single
+// translation unit (e.g. old.cc). We don't reuse CallGraph in clang/Analysis
+// because that CallGraph only supports function declarations.
+//
+// Helper declarations include following types:
+// * function/variable/class definitions in an anonymous namespace.
+// * static function/variable definitions in a global/named namespace.
+//
+// The reference graph is a directed graph. Each node in the graph represents a
+// helper declaration in old.cc or a non-moved/moved declaration (e.g. class,
+// function) in old.h, which means each node is associated with a Decl.
+//
+// To construct the graph, we use AST matcher to find interesting Decls (usually
+// a pair of Caller and Callee), and add an edge from the Caller node to the
+// Callee node.
+//
+// Specially, for a class, it might have multiple declarations such methods
+// and member variables. We only use a single node to present this class, and
+// this node is associated with the class declaration (CXXRecordDecl).
+//
+// The graph has 3 types of edges:
+// 1. moved_decl => helper_decl
+// 2. non_moved_decl => helper_decl
+// 3. helper_decl => helper_decl
+class HelperDeclRefGraph {
+public:
+ HelperDeclRefGraph() = default;
+ ~HelperDeclRefGraph() = default;
+
+ // Add a directed edge from the caller node to the callee node.
+ // A new node will be created if the node for Caller/Callee doesn't exist.
+ //
+ // Note that, all class member declarations are represented by a single node
+ // in the graph. The corresponding Decl of this node is the class declaration.
+ void addEdge(const Decl *Caller, const Decl *Callee);
+ CallGraphNode *getNode(const Decl *D) const;
+
+ // Get all reachable nodes in the graph from the given declaration D's node,
+ // including D.
+ llvm::DenseSet<const CallGraphNode *> getReachableNodes(const Decl *D) const;
+
+ // Dump the call graph for debug purpose.
+ void dump() const;
+
+private:
+ void print(raw_ostream &OS) const;
+ // Lookup a node for the given declaration D. If not found, insert a new
+ // node into the graph.
+ CallGraphNode *getOrInsertNode(Decl *D);
+
+ typedef llvm::DenseMap<const Decl *, std::unique_ptr<CallGraphNode>>
+ DeclMapTy;
+
+ // DeclMap owns all CallGraphNodes.
+ DeclMapTy DeclMap;
+};
+
+// A builder helps to construct a call graph of helper declarations.
+class HelperDeclRGBuilder : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ HelperDeclRGBuilder() : RG(new HelperDeclRefGraph) {}
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ const HelperDeclRefGraph *getGraph() const { return RG.get(); }
+
+ // Find out the outmost enclosing class/function declaration of a given D.
+ // For a CXXMethodDecl, get its CXXRecordDecl; For a VarDecl/FunctionDecl, get
+ // its outmost enclosing FunctionDecl or CXXRecordDecl.
+ // Return D if not found.
+ static const Decl *getOutmostClassOrFunDecl(const Decl *D);
+
+private:
+ std::unique_ptr<HelperDeclRefGraph> RG;
+};
+
+} // namespace move
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-move
+ ClangMoveMain.cpp
+ )
+
+target_link_libraries(clang-move
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangMove
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ )
--- /dev/null
+//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <set>
+#include <string>
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+std::error_code CreateNewFile(const llvm::Twine &path) {
+ int fd = 0;
+ if (std::error_code ec =
+ llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
+ return ec;
+
+ return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
+}
+
+cl::OptionCategory ClangMoveCategory("clang-move options");
+
+cl::list<std::string> Names("names", cl::CommaSeparated,
+ cl::desc("The list of the names of classes being "
+ "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ OldHeader("old_header",
+ cl::desc("The relative/absolute file path of old header."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ NewHeader("new_header",
+ cl::desc("The relative/absolute file path of new header."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+ OldDependOnNew("old_depend_on_new",
+ cl::desc("Whether old header will depend on new header. If "
+ "true, clang-move will "
+ "add #include of new header to old header."),
+ cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+ NewDependOnOld("new_depend_on_old",
+ cl::desc("Whether new header will depend on old header. If "
+ "true, clang-move will "
+ "add #include of old header to new header."),
+ cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+ Style("style",
+ cl::desc("The style name used for reformatting. Default is \"llvm\""),
+ cl::init("llvm"), cl::cat(ClangMoveCategory));
+
+cl::opt<bool> Dump("dump_result",
+ cl::desc("Dump results in JSON format to stdout."),
+ cl::cat(ClangMoveCategory));
+
+cl::opt<bool> DumpDecls(
+ "dump_decls",
+ cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
+ "the option is specified, other command options will be ignored. "
+ "An empty JSON will be returned if old header isn't specified."),
+ cl::cat(ClangMoveCategory));
+
+} // namespace
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+ tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
+
+ if (OldDependOnNew && NewDependOnOld) {
+ llvm::errs() << "Provide either --old_depend_on_new or "
+ "--new_depend_on_old. clang-move doesn't support these two "
+ "options at same time (It will introduce include cycle).\n";
+ return 1;
+ }
+
+ tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+ // Add "-fparse-all-comments" compile option to make clang parse all comments.
+ Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
+ "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {Names.begin(), Names.end()};
+ Spec.OldHeader = OldHeader;
+ Spec.NewHeader = NewHeader;
+ Spec.OldCC = OldCC;
+ Spec.NewCC = NewCC;
+ Spec.OldDependOnNew = OldDependOnNew;
+ Spec.NewDependOnOld = NewDependOnOld;
+
+ llvm::SmallString<128> InitialDirectory;
+ if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
+ llvm::report_fatal_error("Cannot detect current path: " +
+ Twine(EC.message()));
+
+ move::ClangMoveContext Context{Spec, Tool.getReplacements(),
+ InitialDirectory.str(), Style, DumpDecls};
+ move::DeclarationReporter Reporter;
+ move::ClangMoveActionFactory Factory(&Context, &Reporter);
+
+ int CodeStatus = Tool.run(&Factory);
+ if (CodeStatus)
+ return CodeStatus;
+
+ if (DumpDecls) {
+ llvm::outs() << "[\n";
+ const auto &Declarations = Reporter.getDeclarationList();
+ for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
+ llvm::outs() << " {\n";
+ llvm::outs() << " \"DeclarationName\": \"" << I->first << "\",\n";
+ llvm::outs() << " \"DeclarationType\": \"" << I->second << "\"\n";
+ llvm::outs() << " }";
+ // Don't print trailing "," at the end of last element.
+ if (I != std::prev(E))
+ llvm::outs() << ",\n";
+ }
+ llvm::outs() << "\n]\n";
+ return 0;
+ }
+
+ if (!NewCC.empty()) {
+ std::error_code EC = CreateNewFile(NewCC);
+ if (EC) {
+ llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
+ << "\n";
+ return EC.value();
+ }
+ }
+ if (!NewHeader.empty()) {
+ std::error_code EC = CreateNewFile(NewHeader);
+ if (EC) {
+ llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
+ << "\n";
+ return EC.value();
+ }
+ }
+
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+ auto &FileMgr = Tool.getFiles();
+ SourceManager SM(Diagnostics, FileMgr);
+ Rewriter Rewrite(SM, LangOptions());
+
+ if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+ llvm::errs() << "Failed applying all replacements.\n";
+ return 1;
+ }
+
+ if (Dump) {
+ std::set<llvm::StringRef> Files;
+ for (const auto &it : Tool.getReplacements())
+ Files.insert(it.first);
+ auto WriteToJson = [&](llvm::raw_ostream &OS) {
+ OS << "[\n";
+ for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
+ OS << " {\n";
+ OS << " \"FilePath\": \"" << *I << "\",\n";
+ const auto *Entry = FileMgr.getFile(*I);
+ auto ID = SM.translateFile(Entry);
+ std::string Content;
+ llvm::raw_string_ostream ContentStream(Content);
+ Rewrite.getEditBuffer(ID).write(ContentStream);
+ OS << " \"SourceText\": \""
+ << llvm::yaml::escape(ContentStream.str()) << "\"\n";
+ OS << " }";
+ if (I != std::prev(E))
+ OS << ",\n";
+ }
+ OS << "\n]\n";
+ };
+ WriteToJson(llvm::outs());
+ return 0;
+ }
+
+ return Rewrite.overwriteChangedFiles();
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ lineeditor
+ support
+ )
+
+add_clang_library(clangQuery
+ Query.cpp
+ QueryParser.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangDynamicASTMatchers
+ clangFrontend
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===---- Query.cpp - clang-query query -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+Query::~Query() {}
+
+bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ OS << ErrStr << "\n";
+ return false;
+}
+
+bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ return true;
+}
+
+bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ OS << "Available commands:\n\n"
+ " match MATCHER, m MATCHER "
+ "Match the loaded ASTs against the given matcher.\n"
+ " let NAME MATCHER, l NAME MATCHER "
+ "Give a matcher expression a name, to be used later\n"
+ " "
+ "as part of other expressions.\n"
+ " set bind-root (true|false) "
+ "Set whether to bind the root matcher to \"root\".\n"
+ " set output (diag|print|dump) "
+ "Set whether to print bindings as diagnostics,\n"
+ " "
+ "AST pretty prints or AST dumps.\n"
+ " quit "
+ "Terminates the query session.\n\n";
+ return true;
+}
+
+bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ QS.Terminate = true;
+ return true;
+}
+
+namespace {
+
+struct CollectBoundNodes : MatchFinder::MatchCallback {
+ std::vector<BoundNodes> &Bindings;
+ CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
+ void run(const MatchFinder::MatchResult &Result) override {
+ Bindings.push_back(Result.Nodes);
+ }
+};
+
+} // namespace
+
+bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ unsigned MatchCount = 0;
+
+ for (auto &AST : QS.ASTs) {
+ MatchFinder Finder;
+ std::vector<BoundNodes> Matches;
+ DynTypedMatcher MaybeBoundMatcher = Matcher;
+ if (QS.BindRoot) {
+ llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
+ if (M)
+ MaybeBoundMatcher = *M;
+ }
+ CollectBoundNodes Collect(Matches);
+ if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
+ OS << "Not a valid top-level matcher.\n";
+ return false;
+ }
+ Finder.matchAST(AST->getASTContext());
+
+ for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
+ OS << "\nMatch #" << ++MatchCount << ":\n\n";
+
+ for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
+ ++BI) {
+ switch (QS.OutKind) {
+ case OK_Diag: {
+ clang::SourceRange R = BI->second.getSourceRange();
+ if (R.isValid()) {
+ TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
+ &AST->getDiagnostics().getDiagnosticOptions());
+ TD.emitDiagnostic(
+ FullSourceLoc(R.getBegin(), AST->getSourceManager()),
+ DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
+ CharSourceRange::getTokenRange(R), None);
+ }
+ break;
+ }
+ case OK_Print: {
+ OS << "Binding for \"" << BI->first << "\":\n";
+ BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
+ OS << "\n";
+ break;
+ }
+ case OK_Dump: {
+ OS << "Binding for \"" << BI->first << "\":\n";
+ BI->second.dump(OS, AST->getSourceManager());
+ OS << "\n";
+ break;
+ }
+ }
+ }
+
+ if (MI->getMap().empty())
+ OS << "No bindings.\n";
+ }
+ }
+
+ OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
+ return true;
+}
+
+bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ if (Value) {
+ QS.NamedValues[Name] = Value;
+ } else {
+ QS.NamedValues.erase(Name);
+ }
+ return true;
+}
+
+#ifndef _MSC_VER
+const QueryKind SetQueryKind<bool>::value;
+const QueryKind SetQueryKind<OutputKind>::value;
+#endif
+
+} // namespace query
+} // namespace clang
--- /dev/null
+//===--- Query.h - clang-query ----------------------------------*- 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_QUERY_QUERY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
+
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/Optional.h"
+#include <string>
+
+namespace clang {
+namespace query {
+
+enum OutputKind { OK_Diag, OK_Print, OK_Dump };
+
+enum QueryKind {
+ QK_Invalid,
+ QK_NoOp,
+ QK_Help,
+ QK_Let,
+ QK_Match,
+ QK_SetBool,
+ QK_SetOutputKind,
+ QK_Quit
+};
+
+class QuerySession;
+
+struct Query : llvm::RefCountedBase<Query> {
+ Query(QueryKind Kind) : Kind(Kind) {}
+ virtual ~Query();
+
+ /// Perform the query on \p QS and print output to \p OS.
+ ///
+ /// \return false if an error occurs, otherwise return true.
+ virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
+
+ const QueryKind Kind;
+};
+
+typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
+
+/// Any query which resulted in a parse error. The error message is in ErrStr.
+struct InvalidQuery : Query {
+ InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ std::string ErrStr;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
+};
+
+/// No-op query (i.e. a blank line).
+struct NoOpQuery : Query {
+ NoOpQuery() : Query(QK_NoOp) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
+};
+
+/// Query for "help".
+struct HelpQuery : Query {
+ HelpQuery() : Query(QK_Help) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
+};
+
+/// Query for "quit".
+struct QuitQuery : Query {
+ QuitQuery() : Query(QK_Quit) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_Quit; }
+};
+
+/// Query for "match MATCHER".
+struct MatchQuery : Query {
+ MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
+ : Query(QK_Match), Matcher(Matcher) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ ast_matchers::dynamic::DynTypedMatcher Matcher;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
+};
+
+struct LetQuery : Query {
+ LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
+ : Query(QK_Let), Name(Name), Value(Value) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+ std::string Name;
+ ast_matchers::dynamic::VariantValue Value;
+
+ static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
+};
+
+template <typename T> struct SetQueryKind {};
+
+template <> struct SetQueryKind<bool> {
+ static const QueryKind value = QK_SetBool;
+};
+
+template <> struct SetQueryKind<OutputKind> {
+ static const QueryKind value = QK_SetOutputKind;
+};
+
+/// Query for "set VAR VALUE".
+template <typename T> struct SetQuery : Query {
+ SetQuery(T QuerySession::*Var, T Value)
+ : Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
+ bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
+ QS.*Var = Value;
+ return true;
+ }
+
+ static bool classof(const Query *Q) {
+ return Q->Kind == SetQueryKind<T>::value;
+ }
+
+ T QuerySession::*Var;
+ T Value;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
--- /dev/null
+//===---- QueryParser.cpp - clang-query command parser --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/Basic/CharInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <set>
+
+using namespace llvm;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+// Lex any amount of whitespace followed by a "word" (any sequence of
+// non-whitespace characters) from the start of region [Begin,End). If no word
+// is found before End, return StringRef(). Begin is adjusted to exclude the
+// lexed region.
+StringRef QueryParser::lexWord() {
+ while (true) {
+ if (Begin == End)
+ return StringRef(Begin, 0);
+
+ if (!isWhitespace(*Begin))
+ break;
+
+ ++Begin;
+ }
+
+ const char *WordBegin = Begin;
+
+ while (true) {
+ ++Begin;
+
+ if (Begin == End || isWhitespace(*Begin))
+ return StringRef(WordBegin, Begin - WordBegin);
+ }
+}
+
+// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
+// function for details.
+template <typename T> struct QueryParser::LexOrCompleteWord {
+ StringSwitch<T> Switch;
+
+ QueryParser *P;
+ StringRef Word;
+ // Set to the completion point offset in Word, or StringRef::npos if
+ // completion point not in Word.
+ size_t WordCompletionPos;
+
+ LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
+ : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
+
+ template <unsigned N>
+ LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
+ bool IsCompletion = true) {
+ StringRef CaseStr(S, N - 1);
+
+ if (WordCompletionPos == StringRef::npos)
+ Switch.Case(S, Value);
+ else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
+ CaseStr.substr(0, WordCompletionPos) ==
+ Word.substr(0, WordCompletionPos))
+ P->Completions.push_back(LineEditor::Completion(
+ (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
+ return *this;
+ }
+
+ T Default(const T &Value) const { return Switch.Default(Value); }
+};
+
+// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
+// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
+// completions if the lexed word contains the completion point.
+template <typename T>
+QueryParser::LexOrCompleteWord<T>
+QueryParser::lexOrCompleteWord(StringRef &Word) {
+ Word = lexWord();
+ size_t WordCompletionPos = StringRef::npos;
+ if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
+ if (CompletionPos < Word.data())
+ WordCompletionPos = 0;
+ else
+ WordCompletionPos = CompletionPos - Word.data();
+ }
+ return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
+}
+
+QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
+ StringRef ValStr;
+ unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
+ .Case("false", 0)
+ .Case("true", 1)
+ .Default(~0u);
+ if (Value == ~0u) {
+ return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
+ }
+ return new SetQuery<bool>(Var, Value);
+}
+
+QueryRef QueryParser::parseSetOutputKind() {
+ StringRef ValStr;
+ unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
+ .Case("diag", OK_Diag)
+ .Case("print", OK_Print)
+ .Case("dump", OK_Dump)
+ .Default(~0u);
+ if (OutKind == ~0u) {
+ return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
+ ValStr + "'");
+ }
+ return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
+}
+
+QueryRef QueryParser::endQuery(QueryRef Q) {
+ const char *Extra = Begin;
+ if (!lexWord().empty())
+ return new InvalidQuery("unexpected extra input: '" +
+ StringRef(Extra, End - Extra) + "'");
+ return Q;
+}
+
+namespace {
+
+enum ParsedQueryKind {
+ PQK_Invalid,
+ PQK_NoOp,
+ PQK_Help,
+ PQK_Let,
+ PQK_Match,
+ PQK_Set,
+ PQK_Unlet,
+ PQK_Quit
+};
+
+enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
+
+QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
+ std::string ErrStr;
+ llvm::raw_string_ostream OS(ErrStr);
+ Diag.printToStreamFull(OS);
+ return new InvalidQuery(OS.str());
+}
+
+} // namespace
+
+QueryRef QueryParser::completeMatcherExpression() {
+ std::vector<MatcherCompletion> Comps = Parser::completeExpression(
+ StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
+ &QS.NamedValues);
+ for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
+ Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
+ }
+ return QueryRef();
+}
+
+QueryRef QueryParser::doParse() {
+ StringRef CommandStr;
+ ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
+ .Case("", PQK_NoOp)
+ .Case("help", PQK_Help)
+ .Case("m", PQK_Match, /*IsCompletion=*/false)
+ .Case("let", PQK_Let)
+ .Case("match", PQK_Match)
+ .Case("set", PQK_Set)
+ .Case("unlet", PQK_Unlet)
+ .Case("quit", PQK_Quit)
+ .Default(PQK_Invalid);
+
+ switch (QKind) {
+ case PQK_NoOp:
+ return new NoOpQuery;
+
+ case PQK_Help:
+ return endQuery(new HelpQuery);
+
+ case PQK_Quit:
+ return endQuery(new QuitQuery);
+
+ case PQK_Let: {
+ StringRef Name = lexWord();
+
+ if (Name.empty())
+ return new InvalidQuery("expected variable name");
+
+ if (CompletionPos)
+ return completeMatcherExpression();
+
+ Diagnostics Diag;
+ ast_matchers::dynamic::VariantValue Value;
+ if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
+ &QS.NamedValues, &Value, &Diag)) {
+ return makeInvalidQueryFromDiagnostics(Diag);
+ }
+
+ return new LetQuery(Name, Value);
+ }
+
+ case PQK_Match: {
+ if (CompletionPos)
+ return completeMatcherExpression();
+
+ Diagnostics Diag;
+ Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
+ StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
+ if (!Matcher) {
+ return makeInvalidQueryFromDiagnostics(Diag);
+ }
+ return new MatchQuery(*Matcher);
+ }
+
+ case PQK_Set: {
+ StringRef VarStr;
+ ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
+ .Case("output", PQV_Output)
+ .Case("bind-root", PQV_BindRoot)
+ .Default(PQV_Invalid);
+ if (VarStr.empty())
+ return new InvalidQuery("expected variable name");
+ if (Var == PQV_Invalid)
+ return new InvalidQuery("unknown variable: '" + VarStr + "'");
+
+ QueryRef Q;
+ switch (Var) {
+ case PQV_Output:
+ Q = parseSetOutputKind();
+ break;
+ case PQV_BindRoot:
+ Q = parseSetBool(&QuerySession::BindRoot);
+ break;
+ case PQV_Invalid:
+ llvm_unreachable("Invalid query kind");
+ }
+
+ return endQuery(Q);
+ }
+
+ case PQK_Unlet: {
+ StringRef Name = lexWord();
+
+ if (Name.empty())
+ return new InvalidQuery("expected variable name");
+
+ return endQuery(new LetQuery(Name, VariantValue()));
+ }
+
+ case PQK_Invalid:
+ return new InvalidQuery("unknown command: " + CommandStr);
+ }
+
+ llvm_unreachable("Invalid query kind");
+}
+
+QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
+ return QueryParser(Line, QS).doParse();
+}
+
+std::vector<LineEditor::Completion>
+QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
+ QueryParser P(Line, QS);
+ P.CompletionPos = Line.data() + Pos;
+
+ P.doParse();
+ return P.Completions;
+}
+
+} // namespace query
+} // namespace clang
--- /dev/null
+//===--- QueryParser.h - clang-query ----------------------------*- 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_QUERY_QUERY_PARSER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
+
+#include "Query.h"
+#include "QuerySession.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include <cstddef>
+
+namespace clang {
+namespace query {
+
+class QuerySession;
+
+class QueryParser {
+public:
+ /// Parse \a Line as a query.
+ ///
+ /// \return A QueryRef representing the query, which may be an InvalidQuery.
+ static QueryRef parse(StringRef Line, const QuerySession &QS);
+
+ /// Compute a list of completions for \a Line assuming a cursor at
+ /// \param Pos characters past the start of \a Line, ordered from most
+ /// likely to least likely.
+ ///
+ /// \return A vector of completions for \a Line.
+ static std::vector<llvm::LineEditor::Completion>
+ complete(StringRef Line, size_t Pos, const QuerySession &QS);
+
+private:
+ QueryParser(StringRef Line, const QuerySession &QS)
+ : Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
+
+ StringRef lexWord();
+
+ template <typename T> struct LexOrCompleteWord;
+ template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
+
+ QueryRef parseSetBool(bool QuerySession::*Var);
+ QueryRef parseSetOutputKind();
+ QueryRef completeMatcherExpression();
+
+ QueryRef endQuery(QueryRef Q);
+
+ /// \brief Parse [\p Begin,\p End).
+ ///
+ /// \return A reference to the parsed query object, which may be an
+ /// \c InvalidQuery if a parse error occurs.
+ QueryRef doParse();
+
+ const char *Begin;
+ const char *End;
+
+ const char *CompletionPos;
+ std::vector<llvm::LineEditor::Completion> Completions;
+
+ const QuerySession &QS;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
--- /dev/null
+//===--- QuerySession.h - clang-query ---------------------------*- 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_QUERY_QUERY_SESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
+
+#include "Query.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class ASTUnit;
+
+namespace query {
+
+/// Represents the state for a particular clang-query session.
+class QuerySession {
+public:
+ QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
+ : ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), Terminate(false) {}
+
+ llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
+ OutputKind OutKind;
+ bool BindRoot;
+ bool Terminate;
+ llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-query ClangQuery.cpp)
+target_link_libraries(clang-query
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangDynamicASTMatchers
+ clangFrontend
+ clangQuery
+ clangTooling
+ )
+
+install(TARGETS clang-query RUNTIME DESTINATION bin)
--- /dev/null
+//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool is for interactive exploration of the Clang AST using AST matchers.
+// It currently allows the user to enter a matcher at an interactive prompt and
+// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
+// Example session:
+//
+// $ cat foo.c
+// void foo(void) {}
+// $ clang-query foo.c --
+// clang-query> match functionDecl()
+//
+// Match #1:
+//
+// foo.c:1:1: note: "root" binds here
+// void foo(void) {}
+// ^~~~~~~~~~~~~~~~~
+// 1 match.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QueryParser.h"
+#include "QuerySession.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+#include <fstream>
+#include <string>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+using namespace clang::query;
+using namespace clang::tooling;
+using namespace llvm;
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ClangQueryCategory("clang-query options");
+
+static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
+ cl::value_desc("command"),
+ cl::cat(ClangQueryCategory));
+
+static cl::list<std::string> CommandFiles("f",
+ cl::desc("Read commands from file"),
+ cl::value_desc("file"),
+ cl::cat(ClangQueryCategory));
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+ CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
+
+ if (!Commands.empty() && !CommandFiles.empty()) {
+ llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
+ return 1;
+ }
+
+ ClangTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
+ if (Tool.buildASTs(ASTs) != 0)
+ return 1;
+
+ QuerySession QS(ASTs);
+
+ if (!Commands.empty()) {
+ for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
+ QueryRef Q = QueryParser::parse(*I, QS);
+ if (!Q->run(llvm::outs(), QS))
+ return 1;
+ }
+ } else if (!CommandFiles.empty()) {
+ for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
+ std::ifstream Input(I->c_str());
+ if (!Input.is_open()) {
+ llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
+ return 1;
+ }
+ while (Input.good()) {
+ std::string Line;
+ std::getline(Input, Line);
+
+ QueryRef Q = QueryParser::parse(Line, QS);
+ if (!Q->run(llvm::outs(), QS))
+ return 1;
+ }
+ }
+ } else {
+ LineEditor LE("clang-query");
+ LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
+ return QueryParser::complete(Line, Pos, QS);
+ });
+ while (llvm::Optional<std::string> Line = LE.readLine()) {
+ QueryRef Q = QueryParser::parse(*Line, QS);
+ Q->run(llvm::outs(), QS);
+ llvm::outs().flush();
+ if (QS.Terminate)
+ break;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangReorderFields
+ ReorderFieldsAction.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangIndex
+ clangLex
+ clangToolingCore
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the
+/// ReorderFieldsAction::newASTConsumer method
+///
+//===----------------------------------------------------------------------===//
+
+#include "ReorderFieldsAction.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+#include <algorithm>
+#include <string>
+
+namespace clang {
+namespace reorder_fields {
+using namespace clang::ast_matchers;
+
+/// \brief Finds the definition of a record by name.
+///
+/// \returns nullptr if the name is ambiguous or not found.
+static const CXXRecordDecl *findDefinition(StringRef RecordName,
+ ASTContext &Context) {
+ auto Results = match(
+ recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl"),
+ Context);
+ if (Results.empty()) {
+ llvm::errs() << "Definition of " << RecordName << " not found\n";
+ return nullptr;
+ }
+ if (Results.size() > 1) {
+ llvm::errs() << "The name " << RecordName
+ << " is ambiguous, several definitions found\n";
+ return nullptr;
+ }
+ return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
+}
+
+/// \brief Calculates the new order of fields.
+///
+/// \returns empty vector if the list of fields doesn't match the definition.
+static SmallVector<unsigned, 4>
+getNewFieldsOrder(const CXXRecordDecl *Definition,
+ ArrayRef<std::string> DesiredFieldsOrder) {
+ assert(Definition && "Definition is null");
+
+ llvm::StringMap<unsigned> NameToIndex;
+ for (const auto *Field : Definition->fields())
+ NameToIndex[Field->getName()] = Field->getFieldIndex();
+
+ if (DesiredFieldsOrder.size() != NameToIndex.size()) {
+ llvm::errs() << "Number of provided fields doesn't match definition.\n";
+ return {};
+ }
+ SmallVector<unsigned, 4> NewFieldsOrder;
+ for (const auto &Name : DesiredFieldsOrder) {
+ if (!NameToIndex.count(Name)) {
+ llvm::errs() << "Field " << Name << " not found in definition.\n";
+ return {};
+ }
+ NewFieldsOrder.push_back(NameToIndex[Name]);
+ }
+ assert(NewFieldsOrder.size() == NameToIndex.size());
+ return NewFieldsOrder;
+}
+
+// FIXME: error-handling
+/// \brief Replaces one range of source code by another.
+static void
+addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
+ std::map<std::string, tooling::Replacements> &Replacements) {
+ StringRef NewText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(New),
+ Context.getSourceManager(), Context.getLangOpts());
+ tooling::Replacement R(Context.getSourceManager(),
+ CharSourceRange::getTokenRange(Old), NewText,
+ Context.getLangOpts());
+ consumeError(Replacements[R.getFilePath()].add(R));
+}
+
+/// \brief Reorders fields in the definition of a struct/class.
+///
+/// At the moment reodering of fields with
+/// different accesses (public/protected/private) is not supported.
+/// \returns true on success.
+static bool reorderFieldsInDefinition(
+ const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
+ const ASTContext &Context,
+ std::map<std::string, tooling::Replacements> &Replacements) {
+ assert(Definition && "Definition is null");
+
+ SmallVector<const FieldDecl *, 10> Fields;
+ for (const auto *Field : Definition->fields())
+ Fields.push_back(Field);
+
+ // Check that the permutation of the fields doesn't change the accesses
+ for (const auto *Field : Definition->fields()) {
+ const auto FieldIndex = Field->getFieldIndex();
+ if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
+ llvm::errs() << "Currently reodering of fields with different accesses "
+ "is not supported\n";
+ return false;
+ }
+ }
+
+ for (const auto *Field : Definition->fields()) {
+ const auto FieldIndex = Field->getFieldIndex();
+ if (FieldIndex == NewFieldsOrder[FieldIndex])
+ continue;
+ addReplacement(Field->getSourceRange(),
+ Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
+ Context, Replacements);
+ }
+ return true;
+}
+
+/// \brief Reorders initializers in a C++ struct/class constructor.
+///
+/// A constructor can have initializers for an arbitrary subset of the class's fields.
+/// Thus, we need to ensure that we reorder just the initializers that are present.
+static void reorderFieldsInConstructor(
+ const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
+ const ASTContext &Context,
+ std::map<std::string, tooling::Replacements> &Replacements) {
+ assert(CtorDecl && "Constructor declaration is null");
+ if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
+ return;
+
+ // The method FunctionDecl::isThisDeclarationADefinition returns false
+ // for a defaulted function unless that function has been implicitly defined.
+ // Thus this assert needs to be after the previous checks.
+ assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
+
+ SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
+ for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
+ NewFieldsPositions[NewFieldsOrder[i]] = i;
+
+ SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
+ SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
+ for (const auto *Initializer : CtorDecl->inits()) {
+ if (!Initializer->isWritten())
+ continue;
+ OldWrittenInitializersOrder.push_back(Initializer);
+ NewWrittenInitializersOrder.push_back(Initializer);
+ }
+ auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
+ const CXXCtorInitializer *RHS) {
+ assert(LHS && RHS);
+ return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
+ NewFieldsPositions[RHS->getMember()->getFieldIndex()];
+ };
+ std::sort(std::begin(NewWrittenInitializersOrder),
+ std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
+ assert(OldWrittenInitializersOrder.size() ==
+ NewWrittenInitializersOrder.size());
+ for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
+ if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
+ addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
+ NewWrittenInitializersOrder[i]->getSourceRange(), Context,
+ Replacements);
+}
+
+/// \brief Reorders initializers in the brace initialization of an aggregate.
+///
+/// At the moment partial initialization is not supported.
+/// \returns true on success
+static bool reorderFieldsInInitListExpr(
+ const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
+ const ASTContext &Context,
+ std::map<std::string, tooling::Replacements> &Replacements) {
+ assert(InitListEx && "Init list expression is null");
+ // We care only about InitListExprs which originate from source code.
+ // Implicit InitListExprs are created by the semantic analyzer.
+ if (!InitListEx->isExplicit())
+ return true;
+ // The method InitListExpr::getSyntacticForm may return nullptr indicating that
+ // the current initializer list also serves as its syntactic form.
+ if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
+ InitListEx = SyntacticForm;
+ // If there are no initializers we do not need to change anything.
+ if (!InitListEx->getNumInits())
+ return true;
+ if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
+ llvm::errs() << "Currently only full initialization is supported\n";
+ return false;
+ }
+ for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
+ if (i != NewFieldsOrder[i])
+ addReplacement(
+ InitListEx->getInit(i)->getSourceRange(),
+ InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
+ Replacements);
+ return true;
+}
+
+namespace {
+class ReorderingConsumer : public ASTConsumer {
+ StringRef RecordName;
+ ArrayRef<std::string> DesiredFieldsOrder;
+ std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+ ReorderingConsumer(StringRef RecordName,
+ ArrayRef<std::string> DesiredFieldsOrder,
+ std::map<std::string, tooling::Replacements> &Replacements)
+ : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+ Replacements(Replacements) {}
+
+ ReorderingConsumer(const ReorderingConsumer &) = delete;
+ ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ const CXXRecordDecl *RD = findDefinition(RecordName, Context);
+ if (!RD)
+ return;
+ SmallVector<unsigned, 4> NewFieldsOrder =
+ getNewFieldsOrder(RD, DesiredFieldsOrder);
+ if (NewFieldsOrder.empty())
+ return;
+ if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
+ return;
+ for (const auto *C : RD->ctors())
+ if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
+ reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
+ NewFieldsOrder, Context, Replacements);
+
+ // We only need to reorder init list expressions for aggregate types.
+ // For other types the order of constructor parameters is used,
+ // which we don't change at the moment.
+ // Now (v0) partial initialization is not supported.
+ if (RD->isAggregate())
+ for (auto Result :
+ match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
+ Context))
+ if (!reorderFieldsInInitListExpr(
+ Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
+ Context, Replacements)) {
+ Replacements.clear();
+ return;
+ }
+ }
+};
+} // end anonymous namespace
+
+std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
+ return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
+ Replacements);
+}
+
+} // namespace reorder_fields
+} // namespace clang
--- /dev/null
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.h -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declarations of the ReorderFieldsAction class and
+/// the FieldPosition struct.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+
+namespace reorder_fields {
+
+class ReorderFieldsAction {
+ llvm::StringRef RecordName;
+ llvm::ArrayRef<std::string> DesiredFieldsOrder;
+ std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+ ReorderFieldsAction(
+ llvm::StringRef RecordName,
+ llvm::ArrayRef<std::string> DesiredFieldsOrder,
+ std::map<std::string, tooling::Replacements> &Replacements)
+ : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+ Replacements(Replacements) {}
+
+ ReorderFieldsAction(const ReorderFieldsAction &) = delete;
+ ReorderFieldsAction &operator=(const ReorderFieldsAction &) = delete;
+
+ std::unique_ptr<ASTConsumer> newASTConsumer();
+};
+} // namespace reorder_fields
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
--- /dev/null
+add_clang_executable(clang-reorder-fields ClangReorderFields.cpp)
+
+target_link_libraries(clang-reorder-fields
+ clangBasic
+ clangFrontend
+ clangReorderFields
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ )
+
+install(TARGETS clang-reorder-fields RUNTIME DESTINATION bin)
--- /dev/null
+//===-- tools/extra/clang-reorder-fields/tool/ClangReorderFields.cpp -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation of clang-reorder-fields tool
+///
+//===----------------------------------------------------------------------===//
+
+#include "../ReorderFieldsAction.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include <cstdlib>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace clang;
+
+cl::OptionCategory ClangReorderFieldsCategory("clang-reorder-fields options");
+
+static cl::opt<std::string>
+ RecordName("record-name", cl::Required,
+ cl::desc("The name of the struct/class."),
+ cl::cat(ClangReorderFieldsCategory));
+
+static cl::list<std::string> FieldsOrder("fields-order", cl::CommaSeparated,
+ cl::OneOrMore,
+ cl::desc("The desired fields order."),
+ cl::cat(ClangReorderFieldsCategory));
+
+static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
+ cl::cat(ClangReorderFieldsCategory));
+
+const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
+
+int main(int argc, const char **argv) {
+ tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
+ Usage);
+
+ auto Files = OP.getSourcePathList();
+ tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+
+ reorder_fields::ReorderFieldsAction Action(RecordName, FieldsOrder,
+ Tool.getReplacements());
+
+ auto Factory = tooling::newFrontendActionFactory(&Action);
+
+ if (Inplace)
+ return Tool.runAndSave(Factory.get());
+
+ int ExitCode = Tool.run(Factory.get());
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+
+ auto &FileMgr = Tool.getFiles();
+ SourceManager Sources(Diagnostics, FileMgr);
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+ Tool.applyAllReplacements(Rewrite);
+
+ for (const auto &File : Files) {
+ const auto *Entry = FileMgr.getFile(File);
+ const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+ Rewrite.getEditBuffer(ID).write(outs());
+ }
+
+ return ExitCode;
+}
--- /dev/null
+obj/\r
+bin/\r
+.vs/\r
+Key.snk\r
+clang-tidy.exe\r
+packages/\r
+*.csproj.user\r
--- /dev/null
+option(BUILD_CLANG_TIDY_VS_PLUGIN "Build clang-tidy VS plugin" OFF)
+if (BUILD_CLANG_TIDY_VS_PLUGIN)
+ add_custom_target(clang_tidy_exe_for_vsix
+ ${CMAKE_COMMAND} -E copy_if_different
+ "${LLVM_TOOLS_BINARY_DIR}/clang-tidy.exe"
+ "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/clang-tidy.exe"
+ DEPENDS clang-tidy)
+
+ add_custom_target(clang_tidy_license
+ ${CMAKE_COMMAND} -E copy_if_different
+ "${CLANG_SOURCE_DIR}/LICENSE.TXT"
+ "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/license.txt")
+
+ if (NOT CLANG_TIDY_VS_VERSION)
+ set(CLANG_TIDY_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+ endif()
+
+ configure_file("source.extension.vsixmanifest.in"
+ "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest")
+
+ add_custom_target(clang_tidy_vsix ALL
+ devenv "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy.sln" /Build Release
+ DEPENDS clang_tidy_exe_for_vsix "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/bin/Release/ClangTidy.vsix"
+ "${LLVM_TOOLS_BINARY_DIR}/ClangTidy.vsix"
+ DEPENDS clang_tidy_exe_for_vsix clang_tidy_license)
+endif()
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio 14\r
+VisualStudioVersion = 14.0.25123.0\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangTidy", "ClangTidy\ClangTidy.csproj", "{BE261DA1-36C6-449A-95C5-4653A549170A}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|Any CPU = Debug|Any CPU\r
+ Release|Any CPU = Release|Any CPU\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r
+ {BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r
+ {BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r
+ {BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.Build.0 = Release|Any CPU\r
+ EndGlobalSection\r
+ GlobalSection(SolutionProperties) = preSolution\r
+ HideSolutionNode = FALSE\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ /// <summary>\r
+ /// Allows entire categories of properties to be enabled, disabled, or inherited\r
+ /// in one fell swoop. We add properties to each category with the value being\r
+ /// this enum, and when the value is selected, we use reflection to find all other\r
+ /// properties in the same category and perform the corresponding action.\r
+ /// </summary>\r
+ public enum CategoryVerb\r
+ {\r
+ None,\r
+ Disable,\r
+ Enable,\r
+ Inherit\r
+ }\r
+\r
+ public class CategoryVerbConverter : EnumConverter\r
+ {\r
+ public CategoryVerbConverter() : base(typeof(CategoryVerb))\r
+ {\r
+ }\r
+\r
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)\r
+ {\r
+ if (value is string)\r
+ {\r
+ switch ((string)value)\r
+ {\r
+ case "Disable Category":\r
+ return CategoryVerb.Disable;\r
+ case "Enable Category":\r
+ return CategoryVerb.Enable;\r
+ case "Inherit Category":\r
+ return CategoryVerb.Inherit;\r
+ case "":\r
+ return CategoryVerb.None;\r
+ }\r
+ }\r
+ return base.ConvertFrom(context, culture, value);\r
+ }\r
+\r
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)\r
+ {\r
+ if (value is CategoryVerb && destinationType == typeof(string))\r
+ {\r
+ switch ((CategoryVerb)value)\r
+ {\r
+ case CategoryVerb.Disable:\r
+ return "Disable Category";\r
+ case CategoryVerb.Enable:\r
+ return "Enable Category";\r
+ case CategoryVerb.Inherit:\r
+ return "Inherit Category";\r
+ case CategoryVerb.None:\r
+ return String.Empty;\r
+ }\r
+ }\r
+\r
+ return base.ConvertTo(context, culture, value, destinationType);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using YamlDotNet.Serialization;\r
+using YamlDotNet.Serialization.NamingConventions;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ public class CheckInfo\r
+ {\r
+ [YamlAlias("Name")]\r
+ public string Name { get; set; }\r
+\r
+ [YamlAlias("Label")]\r
+ public string Label { get; set; }\r
+\r
+ [YamlAlias("Description")]\r
+ public string Desc { get; set; }\r
+\r
+ [YamlAlias("Category")]\r
+ public string Category { get; set; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Reads the list of checks from Yaml and builds a description of each one.\r
+ /// This list of checks is then used by the PropertyGrid to determine what\r
+ /// items to display.\r
+ /// </summary>\r
+ public static class CheckDatabase\r
+ {\r
+ static CheckInfo[] Checks_ = null;\r
+\r
+ class CheckRoot\r
+ {\r
+ [YamlAlias("Checks")]\r
+ public CheckInfo[] Checks { get; set; }\r
+ }\r
+\r
+ static CheckDatabase()\r
+ {\r
+ using (StringReader Reader = new StringReader(Resources.ClangTidyChecks))\r
+ {\r
+ Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());\r
+ var Root = D.Deserialize<CheckRoot>(Reader);\r
+ Checks_ = Root.Checks;\r
+\r
+ HashSet<string> Names = new HashSet<string>();\r
+ foreach (var Check in Checks_)\r
+ {\r
+ if (Names.Contains(Check.Name))\r
+ continue;\r
+ Names.Add(Check.Name);\r
+ }\r
+ }\r
+ }\r
+\r
+ public static IEnumerable<CheckInfo> Checks\r
+ {\r
+ get\r
+ {\r
+ return Checks_;\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ /// <summary>\r
+ /// CheckTree is used to group checks into categories and subcategories. For\r
+ /// example, given the following list of checks:\r
+ /// \r
+ /// llvm-include-order\r
+ /// llvm-namespace-comment\r
+ /// llvm-twine-local\r
+ /// llvm-header-guard\r
+ /// google-runtime-member-string-references\r
+ /// google-runtime-int\r
+ /// google-readability-namespace-comments\r
+ /// \r
+ /// the corresponding CheckTree would look like this:\r
+ /// \r
+ /// llvm\r
+ /// include-order\r
+ /// namespace-comment\r
+ /// twine-local\r
+ /// header-guard\r
+ /// google\r
+ /// runtime\r
+ /// member-string-references\r
+ /// int\r
+ /// readability\r
+ /// namespace-comments\r
+ /// redundant-smartptr-get\r
+ /// \r
+ /// This is useful when serializing a set of options out to a .clang-tidy file,\r
+ /// because we need to decide the most efficient way to serialize the sequence\r
+ /// of check commands, when to use wildcards, etc. For example, if everything\r
+ /// under google is inherited, we can simply leave that entry out entirely from\r
+ /// the .clang-tidy file. On the other hand, if anything is inherited, we *must\r
+ /// not* add or remove google-* by wildcard because that, by definition, means\r
+ /// the property is no longer inherited. When we can categorize the checks into\r
+ /// groups and subgroups like this, it is possible to efficiently serialize to\r
+ /// a minimal representative .clang-tidy file.\r
+ /// </summary>\r
+\r
+ public abstract class CheckTreeNode\r
+ {\r
+ private string Name_;\r
+ private CheckTreeNode Parent_;\r
+\r
+ protected CheckTreeNode(string Name, CheckTreeNode Parent)\r
+ {\r
+ Name_ = Name;\r
+ Parent_ = Parent;\r
+ }\r
+\r
+ public string Path\r
+ {\r
+ get\r
+ {\r
+ if (Parent_ == null)\r
+ return null;\r
+ string ParentPath = Parent_.Path;\r
+ if (ParentPath == null)\r
+ return Name_;\r
+ return ParentPath + "-" + Name_;\r
+ }\r
+ }\r
+\r
+ public string Name\r
+ {\r
+ get\r
+ {\r
+ return Name_;\r
+ }\r
+ }\r
+\r
+\r
+ public abstract int CountChecks { get; }\r
+ public abstract int CountExplicitlyDisabledChecks { get; }\r
+ public abstract int CountExplicitlyEnabledChecks { get; }\r
+ public abstract int CountInheritedChecks { get; }\r
+ }\r
+\r
+ public class CheckTree : CheckTreeNode\r
+ {\r
+ private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>();\r
+ public CheckTree()\r
+ : base(null, null)\r
+ {\r
+\r
+ }\r
+\r
+ private CheckTree(string Name, CheckTree Parent)\r
+ : base(Name, Parent)\r
+ {\r
+ }\r
+\r
+ private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)\r
+ {\r
+ Children_[Name] = new CheckLeaf(Name, this, Property);\r
+ }\r
+\r
+ private CheckTree AddOrCreateSubgroup(string Name)\r
+ {\r
+ CheckTreeNode Subgroup = null;\r
+ if (Children_.TryGetValue(Name, out Subgroup))\r
+ {\r
+ System.Diagnostics.Debug.Assert(Subgroup is CheckTree);\r
+ return (CheckTree)Subgroup;\r
+ }\r
+\r
+ CheckTree SG = new CheckTree(Name, this);\r
+ Children_[Name] = SG;\r
+ return SG;\r
+ }\r
+\r
+ public static CheckTree Build(ClangTidyProperties Config)\r
+ {\r
+ // Since some check names contain dashes in them, it doesn't make sense to\r
+ // simply split all check names by dash and construct a huge tree. For\r
+ // example, in the check called google-runtime-member-string-references,\r
+ // we don't need each of those to be a different subgroup. So instead we\r
+ // explicitly specify the common breaking points at which a user might want\r
+ // to use a -* and everything else falls as a leaf under one of these\r
+ // categories.\r
+ // FIXME: This should be configurable without recompilation\r
+ CheckTree Root = new CheckTree();\r
+ string[][] Groups = new string[][] {\r
+ new string[] {"boost"},\r
+ new string[] {"cert"},\r
+ new string[] {"clang", "diagnostic"},\r
+ new string[] {"cppcoreguidelines", "interfaces"},\r
+ new string[] {"cppcoreguidelines", "pro", "bounds"},\r
+ new string[] {"cppcoreguidelines", "pro", "type"},\r
+ new string[] {"google", "build"},\r
+ new string[] {"google", "readability"},\r
+ new string[] {"google", "runtime"},\r
+ new string[] {"llvm"},\r
+ new string[] {"misc"},\r
+ };\r
+\r
+ foreach (string[] Group in Groups)\r
+ {\r
+ CheckTree Subgroup = Root;\r
+ foreach (string Component in Group)\r
+ Subgroup = Subgroup.AddOrCreateSubgroup(Component);\r
+ }\r
+\r
+ var Props = Config.GetProperties()\r
+ .Cast<PropertyDescriptor>()\r
+ .OfType<DynamicPropertyDescriptor<bool>>()\r
+ .Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0)\r
+ .Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>(\r
+ x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName));\r
+ var PropArray = Props.ToArray();\r
+ foreach (var CheckInfo in PropArray)\r
+ {\r
+ string LeafName = null;\r
+ CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName);\r
+ Tree.AddLeaf(LeafName, CheckInfo.Key);\r
+ }\r
+ return Root;\r
+ }\r
+\r
+ private CheckTree LocateCheckLeafGroup(string Check, out string LeafName)\r
+ {\r
+ string[] Components = Check.Split('-');\r
+ string FirstComponent = Components.FirstOrDefault();\r
+ if (FirstComponent == null)\r
+ {\r
+ LeafName = Check;\r
+ return this;\r
+ }\r
+\r
+ CheckTreeNode Subgroup = null;\r
+ if (!Children_.TryGetValue(FirstComponent, out Subgroup))\r
+ {\r
+ LeafName = Check;\r
+ return this;\r
+ }\r
+ System.Diagnostics.Debug.Assert(Subgroup is CheckTree);\r
+ CheckTree Child = (CheckTree)Subgroup;\r
+ string ChildName = Check.Substring(FirstComponent.Length + 1);\r
+ return Child.LocateCheckLeafGroup(ChildName, out LeafName);\r
+ }\r
+\r
+ public override int CountChecks\r
+ {\r
+ get\r
+ {\r
+ return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; });\r
+ }\r
+ }\r
+\r
+ public override int CountExplicitlyDisabledChecks\r
+ {\r
+ get\r
+ {\r
+ return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; });\r
+ }\r
+ }\r
+\r
+ public override int CountExplicitlyEnabledChecks\r
+ {\r
+ get\r
+ {\r
+ return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; });\r
+ }\r
+ }\r
+ public override int CountInheritedChecks\r
+ {\r
+ get\r
+ {\r
+ return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; });\r
+ }\r
+ }\r
+\r
+ public IDictionary<string, CheckTreeNode> Children\r
+ {\r
+ get { return Children_; }\r
+ }\r
+ }\r
+\r
+ public class CheckLeaf : CheckTreeNode\r
+ {\r
+ private DynamicPropertyDescriptor<bool> Property_;\r
+\r
+ public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)\r
+ : base(Name, Parent)\r
+ {\r
+ Property_ = Property;\r
+ }\r
+\r
+ public override int CountChecks\r
+ {\r
+ get\r
+ {\r
+ return 1;\r
+ }\r
+ }\r
+\r
+ public override int CountExplicitlyDisabledChecks\r
+ {\r
+ get\r
+ {\r
+ if (Property_.IsInheriting)\r
+ return 0;\r
+ return (bool)Property_.GetValue(null) ? 0 : 1;\r
+ }\r
+ }\r
+\r
+ public override int CountExplicitlyEnabledChecks\r
+ {\r
+ get\r
+ {\r
+ if (Property_.IsInheriting)\r
+ return 0;\r
+ return (bool)Property_.GetValue(null) ? 1 : 0;\r
+ }\r
+ }\r
+\r
+ public override int CountInheritedChecks\r
+ {\r
+ get\r
+ {\r
+ return (Property_.IsInheriting) ? 1 : 0;\r
+ }\r
+ }\r
+\r
+ }\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">\r
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />\r
+ <PropertyGroup>\r
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>\r
+ <SchemaVersion>2.0</SchemaVersion>\r
+ <ProjectGuid>{BE261DA1-36C6-449A-95C5-4653A549170A}</ProjectGuid>\r
+ <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\r
+ <OutputType>Library</OutputType>\r
+ <AppDesignerFolder>Properties</AppDesignerFolder>\r
+ <RootNamespace>LLVM.ClangTidy</RootNamespace>\r
+ <AssemblyName>ClangTidy</AssemblyName>\r
+ <SignAssembly>true</SignAssembly>\r
+ <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>\r
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\r
+ <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>\r
+ <FileUpgradeFlags>\r
+ </FileUpgradeFlags>\r
+ <UpgradeBackupLocation>\r
+ </UpgradeBackupLocation>\r
+ <OldToolsVersion>4.0</OldToolsVersion>\r
+ <PublishUrl>publish\</PublishUrl>\r
+ <Install>true</Install>\r
+ <InstallFrom>Disk</InstallFrom>\r
+ <UpdateEnabled>false</UpdateEnabled>\r
+ <UpdateMode>Foreground</UpdateMode>\r
+ <UpdateInterval>7</UpdateInterval>\r
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>\r
+ <UpdatePeriodically>false</UpdatePeriodically>\r
+ <UpdateRequired>false</UpdateRequired>\r
+ <MapFileExtensions>true</MapFileExtensions>\r
+ <ApplicationRevision>0</ApplicationRevision>\r
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\r
+ <IsWebBootstrapper>false</IsWebBootstrapper>\r
+ <UseApplicationTrust>false</UseApplicationTrust>\r
+ <BootstrapperEnabled>true</BootstrapperEnabled>\r
+ <TargetFrameworkProfile />\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
+ <DebugSymbols>true</DebugSymbols>\r
+ <DebugType>full</DebugType>\r
+ <Optimize>false</Optimize>\r
+ <OutputPath>bin\Debug\</OutputPath>\r
+ <DefineConstants>DEBUG;TRACE</DefineConstants>\r
+ <ErrorReport>prompt</ErrorReport>\r
+ <WarningLevel>0</WarningLevel>\r
+ <Prefer32Bit>false</Prefer32Bit>\r
+ <PlatformTarget>AnyCPU</PlatformTarget>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
+ <DebugType>pdbonly</DebugType>\r
+ <Optimize>true</Optimize>\r
+ <OutputPath>bin\Release\</OutputPath>\r
+ <DefineConstants>TRACE</DefineConstants>\r
+ <ErrorReport>prompt</ErrorReport>\r
+ <WarningLevel>4</WarningLevel>\r
+ <RunCodeAnalysis>true</RunCodeAnalysis>\r
+ <Prefer32Bit>false</Prefer32Bit>\r
+ </PropertyGroup>\r
+ <ItemGroup>\r
+ <Reference Include="Microsoft.CSharp" />\r
+ <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="Microsoft.VisualStudio.OLE.Interop" />\r
+ <Reference Include="Microsoft.VisualStudio.Settings.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86" />\r
+ <Reference Include="Microsoft.VisualStudio.Shell.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop" />\r
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />\r
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />\r
+ <Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+ <Reference Include="System" />\r
+ <Reference Include="System.Core" />\r
+ <Reference Include="System.Data" />\r
+ <Reference Include="System.Design" />\r
+ <Reference Include="System.Drawing" />\r
+ <Reference Include="System.Windows.Forms" />\r
+ <Reference Include="System.Xml" />\r
+ <Reference Include="System.Xml.Linq" />\r
+ <Reference Include="YamlDotNet, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">\r
+ <HintPath>..\packages\YamlDotNet.3.3.0\lib\net35\YamlDotNet.dll</HintPath>\r
+ <Private>True</Private>\r
+ </Reference>\r
+ <Reference Include="YamlDotNet.Dynamic, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">\r
+ <HintPath>..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll</HintPath>\r
+ <Private>True</Private>\r
+ </Reference>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <COMReference Include="EnvDTE">\r
+ <Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>\r
+ <VersionMajor>8</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ <COMReference Include="EnvDTE100">\r
+ <Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>\r
+ <VersionMajor>10</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ <COMReference Include="EnvDTE80">\r
+ <Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>\r
+ <VersionMajor>8</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ <COMReference Include="EnvDTE90">\r
+ <Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>\r
+ <VersionMajor>9</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ <COMReference Include="Microsoft.VisualStudio.CommandBars">\r
+ <Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>\r
+ <VersionMajor>8</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ <COMReference Include="stdole">\r
+ <Guid>{00020430-0000-0000-C000-000000000046}</Guid>\r
+ <VersionMajor>2</VersionMajor>\r
+ <VersionMinor>0</VersionMinor>\r
+ <Lcid>0</Lcid>\r
+ <WrapperTool>primary</WrapperTool>\r
+ <Isolated>False</Isolated>\r
+ <EmbedInteropTypes>False</EmbedInteropTypes>\r
+ </COMReference>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Compile Include="CategoryVerb.cs" />\r
+ <Compile Include="CheckDatabase.cs" />\r
+ <Compile Include="CheckTree.cs" />\r
+ <Compile Include="ClangTidyCheckAttribute.cs" />\r
+ <Compile Include="ClangTidyConfigurationPage.cs">\r
+ <SubType>Component</SubType>\r
+ </Compile>\r
+ <Compile Include="ClangTidyProperties.cs">\r
+ <SubType>Component</SubType>\r
+ </Compile>\r
+ <Compile Include="DynamicPropertyConverter.cs" />\r
+ <Compile Include="DynamicPropertyDescriptor.cs" />\r
+ <Compile Include="ForwardingPropertyDescriptor.cs" />\r
+ <Compile Include="Guids.cs" />\r
+ <Compile Include="DynamicPropertyComponent.cs">\r
+ <SubType>Component</SubType>\r
+ </Compile>\r
+ <Compile Include="DynamicPropertyComponent.Designer.cs" />\r
+ <Compile Include="ClangTidyConfigParser.cs" />\r
+ <Compile Include="Resources.Designer.cs">\r
+ <AutoGen>True</AutoGen>\r
+ <DesignTime>True</DesignTime>\r
+ <DependentUpon>Resources.resx</DependentUpon>\r
+ </Compile>\r
+ <Compile Include="GlobalSuppressions.cs" />\r
+ <Compile Include="ClangTidyPackage.cs" />\r
+ <Compile Include="Properties\AssemblyInfo.cs" />\r
+ <Compile Include="PkgCmdID.cs" />\r
+ <Compile Include="ClangTidyPropertyGrid.cs">\r
+ <SubType>UserControl</SubType>\r
+ </Compile>\r
+ <Compile Include="ClangTidyPropertyGrid.Designer.cs">\r
+ <DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>\r
+ </Compile>\r
+ <Compile Include="Utility.cs" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <EmbeddedResource Include="Resources.resx">\r
+ <Generator>ResXFileCodeGenerator</Generator>\r
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>\r
+ <SubType>Designer</SubType>\r
+ </EmbeddedResource>\r
+ <EmbeddedResource Include="ClangTidyPropertyGrid.resx">\r
+ <DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>\r
+ </EmbeddedResource>\r
+ <EmbeddedResource Include="VSPackage.resx">\r
+ <MergeWithCTO>true</MergeWithCTO>\r
+ <ManifestResourceName>VSPackage</ManifestResourceName>\r
+ </EmbeddedResource>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <None Include="Key.snk" />\r
+ <None Include="packages.config" />\r
+ <None Include="Resources\ClangTidyChecks.yaml" />\r
+ <None Include="source.extension.vsixmanifest">\r
+ <SubType>Designer</SubType>\r
+ </None>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <VSCTCompile Include="ClangTidy.vsct">\r
+ <ResourceName>Menus.ctmenu</ResourceName>\r
+ <SubType>Designer</SubType>\r
+ </VSCTCompile>\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <None Include="Resources\Images_32bit.bmp" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Content Include="clang-tidy.exe">\r
+ <IncludeInVSIX>true</IncludeInVSIX>\r
+ </Content>\r
+ <Content Include="license.txt">\r
+ <IncludeInVSIX>true</IncludeInVSIX>\r
+ </Content>\r
+ <Content Include="Resources\Package.ico" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <BootstrapperPackage Include=".NETFramework,Version=v4.0">\r
+ <Visible>False</Visible>\r
+ <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>\r
+ <Install>true</Install>\r
+ </BootstrapperPackage>\r
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">\r
+ <Visible>False</Visible>\r
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>\r
+ <Install>false</Install>\r
+ </BootstrapperPackage>\r
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">\r
+ <Visible>False</Visible>\r
+ <ProductName>.NET Framework 3.5 SP1</ProductName>\r
+ <Install>false</Install>\r
+ </BootstrapperPackage>\r
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">\r
+ <Visible>False</Visible>\r
+ <ProductName>Windows Installer 4.5</ProductName>\r
+ <Install>true</Install>\r
+ </BootstrapperPackage>\r
+ </ItemGroup>\r
+ <PropertyGroup>\r
+ <UseCodebase>true</UseCodebase>\r
+ </PropertyGroup>\r
+ <PropertyGroup>\r
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>\r
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>\r
+ </PropertyGroup>\r
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />\r
+ <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />\r
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />\r
+ <PropertyGroup>\r
+ <PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(SDKToolsPath)\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>\r
+ </PropertyGroup>\r
+ <Target Name="BeforeBuild">\r
+ <Exec ContinueOnError="false" Command=""..\packages\Brutal.Dev.StrongNameSigner.1.8.0\tools\StrongNameSigner.Console.exe" -in "..\packages" -l Summary" />\r
+ </Target>\r
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
+ Other similar extension points exist, see Microsoft.Common.targets.\r
+ <Target Name="AfterBuild">\r
+ </Target>\r
+ -->\r
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">\r
+\r
+ <!-- This is the file that defines the actual layout and type of the commands.\r
+ It is divided in different sections (e.g. command definition, command\r
+ placement, ...), with each defining a specific set of properties.\r
+ See the comment before each section for more details about how to\r
+ use it. -->\r
+\r
+ <!-- The VSCT compiler (the tool that translates this file into the binary \r
+ format that VisualStudio will consume) has the ability to run a preprocessor \r
+ on the vsct file; this preprocessor is (usually) the C++ preprocessor, so \r
+ it is possible to define includes and macros with the same syntax used \r
+ in C++ files. Using this ability of the compiler here, we include some files \r
+ defining some of the constants that we will use inside the file. -->\r
+\r
+ <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->\r
+ <Extern href="stdidcmd.h"/>\r
+\r
+ <!--This header contains the command ids for the menus provided by the shell. -->\r
+ <Extern href="vsshlids.h"/>\r
+\r
+\r
+\r
+\r
+ <!--The Commands section is where we the commands, menus and menu groups are defined.\r
+ This section uses a Guid to identify the package that provides the command defined inside it. -->\r
+ <Commands package="guidClangTidyPkg">\r
+ <!-- Inside this section we have different sub-sections: one for the menus, another \r
+ for the menu groups, one for the buttons (the actual commands), one for the combos \r
+ and the last one for the bitmaps used. Each element is identified by a command id that \r
+ is a unique pair of guid and numeric identifier; the guid part of the identifier is usually \r
+ called "command set" and is used to group different command inside a logically related \r
+ group; your package should define its own command set in order to avoid collisions \r
+ with command ids defined by other packages. -->\r
+\r
+ \r
+ <!-- In this section you can define new menu groups. A menu group is a container for \r
+ other menus or buttons (commands); from a visual point of view you can see the \r
+ group as the part of a menu contained between two lines. The parent of a group \r
+ must be a menu. -->\r
+ <Groups>\r
+\r
+ <Group guid="guidClangTidyCmdSet" id="MyMenuGroup" priority="0x0600">\r
+ <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>\r
+ </Group>\r
+ \r
+\r
+\r
+ </Groups>\r
+ \r
+ <!--Buttons section. -->\r
+ <!--This section defines the elements the user can interact with, like a menu command or a button \r
+ or combo box in a toolbar. -->\r
+ <Buttons>\r
+ <!--To define a menu group you have to specify its ID, the parent menu and its display priority. \r
+ The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use\r
+ the CommandFlag node.\r
+ You can add more than one CommandFlag node e.g.:\r
+ <CommandFlag>DefaultInvisible</CommandFlag>\r
+ <CommandFlag>DynamicVisibility</CommandFlag>\r
+ If you do not want an image next to your command, remove the Icon node /> -->\r
+\r
+ <Button guid="guidClangTidyCmdSet" id="cmdidClangTidy" priority="0x0100" type="Button">\r
+ <Parent guid="guidClangTidyCmdSet" id="MyMenuGroup" />\r
+ <Icon guid="guidImages" id="bmpPic1" />\r
+ <Strings>\r
+ <ButtonText>ClangTidy</ButtonText>\r
+ </Strings>\r
+ </Button>\r
+\r
+\r
+\r
+ </Buttons>\r
+ \r
+ <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->\r
+ <Bitmaps>\r
+ <!-- The bitmap id is defined in a way that is a little bit different from the others: \r
+ the declaration starts with a guid for the bitmap strip, then there is the resource id of the \r
+ bitmap strip containing the bitmaps and then there are the numeric ids of the elements used \r
+ inside a button definition. An important aspect of this declaration is that the element id \r
+ must be the actual index (1-based) of the bitmap inside the bitmap strip. -->\r
+ <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>\r
+ \r
+ </Bitmaps>\r
+ \r
+ </Commands>\r
+\r
+\r
+ <KeyBindings>\r
+ <KeyBinding guid="guidClangTidyCmdSet" id="cmdidClangTidy" editor="guidTextEditor" key1="R" mod1="Control" key2="T" mod2="Control"/>\r
+ </KeyBindings>\r
+\r
+\r
+\r
+ <Symbols>\r
+ <!-- This is the package guid. -->\r
+ <GuidSymbol name="guidClangTidyPkg" value="{AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C}" />\r
+ \r
+ <!-- This is the guid used to group the menu commands together -->\r
+ <GuidSymbol name="guidClangTidyCmdSet" value="{9E0F0493-6493-46DE-AEE1-ACD8F60F265E}">\r
+ <IDSymbol name="MyMenuGroup" value="0x1020" />\r
+ <IDSymbol name="cmdidClangTidy" value="0x0100" />\r
+ </GuidSymbol>\r
+\r
+ <GuidSymbol name="guidTextEditor" value="{E10FAD35-7FB8-4991-A269-EF88F12166C9}" />\r
+\r
+\r
+ <GuidSymbol name="guidImages" value="{942F126F-942D-428A-84B4-4AC7C523D0B2}" >\r
+ <IDSymbol name="bmpPic1" value="1" />\r
+ <IDSymbol name="bmpPic2" value="2" />\r
+ <IDSymbol name="bmpPicSearch" value="3" />\r
+ <IDSymbol name="bmpPicX" value="4" />\r
+ <IDSymbol name="bmpPicArrows" value="5" />\r
+ </GuidSymbol>\r
+ </Symbols>\r
+\r
+</CommandTable>\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ public class ClangTidyCheckAttribute : Attribute\r
+ {\r
+ private string CheckName_;\r
+ public ClangTidyCheckAttribute(string CheckName)\r
+ {\r
+ this.CheckName_ = CheckName;\r
+ }\r
+\r
+ public string CheckName\r
+ {\r
+ get { return CheckName_; }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using YamlDotNet.Serialization;\r
+using YamlDotNet.Serialization.NamingConventions;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ static class ClangTidyConfigParser\r
+ {\r
+ public class CheckOption\r
+ {\r
+ [YamlAlias("key")]\r
+ public string Key { get; set; }\r
+\r
+ [YamlAlias("value")]\r
+ public string Value { get; set; }\r
+ }\r
+ public class ClangTidyYaml\r
+ {\r
+ [YamlAlias("Checks")]\r
+ public string Checks { get; set; }\r
+\r
+ [YamlAlias("CheckOptions")]\r
+ public List<CheckOption> CheckOptions { get; set; }\r
+ }\r
+\r
+ public static List<KeyValuePair<string, ClangTidyProperties>> ParseConfigurationChain(string ClangTidyFile)\r
+ {\r
+ List<KeyValuePair<string, ClangTidyProperties>> Result = new List<KeyValuePair<string, ClangTidyProperties>>();\r
+ Result.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));\r
+\r
+ foreach (string P in Utility.SplitPath(ClangTidyFile).Reverse())\r
+ {\r
+ if (!Utility.HasClangTidyFile(P))\r
+ continue;\r
+\r
+ string ConfigFile = Path.Combine(P, ".clang-tidy");\r
+\r
+ using (StreamReader Reader = new StreamReader(ConfigFile))\r
+ {\r
+ Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());\r
+ ClangTidyYaml Y = D.Deserialize<ClangTidyYaml>(Reader);\r
+ ClangTidyProperties Parent = Result[Result.Count - 1].Value;\r
+ ClangTidyProperties NewProps = new ClangTidyProperties(Parent);\r
+ SetPropertiesFromYaml(Y, NewProps);\r
+ Result.Add(new KeyValuePair<string, ClangTidyProperties>(P, NewProps));\r
+ }\r
+ }\r
+ return Result;\r
+ }\r
+\r
+ enum TreeLevelOp\r
+ {\r
+ Enable,\r
+ Disable,\r
+ Inherit\r
+ }\r
+\r
+ public static void SerializeClangTidyFile(ClangTidyProperties Props, string ClangTidyFilePath)\r
+ {\r
+ List<string> CommandList = new List<string>();\r
+ SerializeCheckTree(CommandList, Props.GetCheckTree(), TreeLevelOp.Inherit);\r
+\r
+ CommandList.Sort((x, y) =>\r
+ {\r
+ bool LeftSub = x.StartsWith("-");\r
+ bool RightSub = y.StartsWith("-");\r
+ if (LeftSub && !RightSub)\r
+ return -1;\r
+ if (RightSub && !LeftSub)\r
+ return 1;\r
+ return StringComparer.CurrentCulture.Compare(x, y);\r
+ });\r
+\r
+ string ConfigFile = Path.Combine(ClangTidyFilePath, ".clang-tidy");\r
+ using (StreamWriter Writer = new StreamWriter(ConfigFile))\r
+ {\r
+ Serializer S = new Serializer(namingConvention: new PascalCaseNamingConvention());\r
+ ClangTidyYaml Yaml = new ClangTidyYaml();\r
+ Yaml.Checks = String.Join(",", CommandList.ToArray());\r
+ S.Serialize(Writer, Yaml);\r
+ }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Convert the given check tree into serialized list of commands that can be written to\r
+ /// the Yaml. The goal here is to determine the minimal sequence of check commands that\r
+ /// will produce the exact configuration displayed in the UI. This is complicated by the\r
+ /// fact that an inherited True is not the same as an explicitly specified True. If the\r
+ /// user has chosen to inherit a setting in a .clang-tidy file, then changing it in the\r
+ /// parent should show the reflected changes in the current file as well. So we cannot\r
+ /// simply -* everything and then add in the checks we need, because -* immediately marks\r
+ /// every single check as explicitly false, thus disabling inheritance.\r
+ /// </summary>\r
+ /// <param name="CommandList">State passed through this recursive algorithm representing\r
+ /// the sequence of commands we have determined so far.\r
+ /// </param>\r
+ /// <param name="Tree">The check tree to serialize. This is the parameter that will be\r
+ /// recursed on as successive subtrees get serialized to `CommandList`.\r
+ /// </param>\r
+ /// <param name="CurrentOp">The current state of the subtree. For example, if the\r
+ /// algorithm decides to -* an entire subtree and then add back one single check,\r
+ /// after adding a -subtree-* command to CommandList, it would pass in a value of\r
+ /// CurrentOp=TreeLevelOp.Disable when it recurses down. This allows deeper iterations\r
+ /// of the algorithm to know what kind of command (if any) needs to be added to CommandList\r
+ /// in order to put a particular check into a particular state.\r
+ /// </param>\r
+ private static void SerializeCheckTree(List<string> CommandList, CheckTree Tree, TreeLevelOp CurrentOp)\r
+ {\r
+ int NumChecks = Tree.CountChecks;\r
+ int NumDisabled = Tree.CountExplicitlyDisabledChecks;\r
+ int NumEnabled = Tree.CountExplicitlyEnabledChecks;\r
+ int NumInherited = Tree.CountInheritedChecks;\r
+\r
+ if (NumChecks == 0)\r
+ return;\r
+\r
+ if (NumInherited > 0)\r
+ System.Diagnostics.Debug.Assert(CurrentOp == TreeLevelOp.Inherit);\r
+\r
+ // If this entire tree is inherited, just exit, nothing about this needs to\r
+ // go in the clang-tidy file.\r
+ if (NumInherited == NumChecks)\r
+ return;\r
+\r
+ TreeLevelOp NewOp = CurrentOp;\r
+ // If there are no inherited properties in this subtree, decide whether to\r
+ // explicitly enable or disable this subtree. Decide by looking at whether\r
+ // there is a larger proportion of disabled or enabled descendants. If\r
+ // there are more disabled items in this subtree for example, disabling the\r
+ // subtree will lead to a smaller configuration file.\r
+ if (NumInherited == 0)\r
+ {\r
+ if (NumDisabled >= NumEnabled)\r
+ NewOp = TreeLevelOp.Disable;\r
+ else\r
+ NewOp = TreeLevelOp.Enable;\r
+ }\r
+\r
+ if (NewOp == TreeLevelOp.Disable)\r
+ {\r
+ // Only add an explicit disable command if the tree was not already disabled\r
+ // to begin with.\r
+ if (CurrentOp != TreeLevelOp.Disable)\r
+ {\r
+ string WildcardPath = "*";\r
+ if (Tree.Path != null)\r
+ WildcardPath = Tree.Path + "-" + WildcardPath;\r
+ CommandList.Add("-" + WildcardPath);\r
+ }\r
+ // If the entire subtree was disabled, there's no point descending.\r
+ if (NumDisabled == NumChecks)\r
+ return;\r
+ }\r
+ else if (NewOp == TreeLevelOp.Enable)\r
+ {\r
+ // Only add an explicit enable command if the tree was not already enabled\r
+ // to begin with. Note that if we're at the root, all checks are already\r
+ // enabled by default, so there's no need to explicitly include *\r
+ if (CurrentOp != TreeLevelOp.Enable && Tree.Path != null)\r
+ {\r
+ string WildcardPath = Tree.Path + "-*";\r
+ CommandList.Add(WildcardPath);\r
+ }\r
+ // If the entire subtree was enabled, there's no point descending.\r
+ if (NumEnabled == NumChecks)\r
+ return;\r
+ }\r
+\r
+ foreach (var Child in Tree.Children)\r
+ {\r
+ if (Child.Value is CheckLeaf)\r
+ {\r
+ CheckLeaf Leaf = (CheckLeaf)Child.Value;\r
+ if (Leaf.CountExplicitlyEnabledChecks == 1 && NewOp != TreeLevelOp.Enable)\r
+ CommandList.Add(Leaf.Path);\r
+ else if (Leaf.CountExplicitlyDisabledChecks == 1 && NewOp != TreeLevelOp.Disable)\r
+ CommandList.Add("-" + Leaf.Path);\r
+ continue;\r
+ }\r
+\r
+ System.Diagnostics.Debug.Assert(Child.Value is CheckTree);\r
+ CheckTree ChildTree = (CheckTree)Child.Value;\r
+ SerializeCheckTree(CommandList, ChildTree, NewOp);\r
+ }\r
+ }\r
+\r
+ private static void SetPropertiesFromYaml(ClangTidyYaml Yaml, ClangTidyProperties Props)\r
+ {\r
+ string[] CheckCommands = Yaml.Checks.Split(',');\r
+ foreach (string Command in CheckCommands)\r
+ {\r
+ if (Command == null || Command.Length == 0)\r
+ continue;\r
+ bool Add = true;\r
+ string Pattern = Command;\r
+ if (Pattern[0] == '-')\r
+ {\r
+ Pattern = Pattern.Substring(1);\r
+ Add = false;\r
+ }\r
+\r
+ foreach (var Match in CheckDatabase.Checks.Where(x => Utility.MatchWildcardString(x.Name, Pattern)))\r
+ {\r
+ Props.SetDynamicValue(Match.Name, Add);\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using Microsoft.VisualStudio;\r
+using Microsoft.VisualStudio.Shell;\r
+using Microsoft.VisualStudio.Shell.Interop;\r
+using System;\r
+using System.Collections;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Runtime.InteropServices;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using System.Windows.Forms;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ [ClassInterface(ClassInterfaceType.AutoDual)]\r
+ [CLSCompliant(false), ComVisible(true)]\r
+ public class ClangTidyConfigurationPage : DialogPage\r
+ {\r
+ ClangTidyPropertyGrid Grid = null;\r
+ protected override IWin32Window Window\r
+ {\r
+ get\r
+ {\r
+ if (Grid == null)\r
+ Grid = new ClangTidyPropertyGrid();\r
+ return Grid;\r
+ }\r
+ }\r
+\r
+ protected override void SaveSetting(PropertyDescriptor property)\r
+ {\r
+ base.SaveSetting(property);\r
+ }\r
+\r
+ public override void SaveSettingsToStorage()\r
+ {\r
+ if (Grid != null)\r
+ Grid.SaveSettingsToStorage();\r
+\r
+ base.SaveSettingsToStorage();\r
+ }\r
+\r
+ public override void ResetSettings()\r
+ {\r
+ base.ResetSettings();\r
+ }\r
+\r
+ protected override void LoadSettingFromStorage(PropertyDescriptor prop)\r
+ {\r
+ base.LoadSettingFromStorage(prop);\r
+ }\r
+\r
+ public override void LoadSettingsFromStorage()\r
+ {\r
+ if (Grid != null)\r
+ Grid.InitializeSettings();\r
+ base.LoadSettingsFromStorage();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+//===-- ClangTidyPackages.cs - VSPackage for clang-tidy ----------*- C# -*-===//\r
+//\r
+// The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+//\r
+// This class contains a VS extension package that runs clang-tidy over a\r
+// file in a VS text editor.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+\r
+using Microsoft.VisualStudio.Editor;\r
+using Microsoft.VisualStudio.Shell;\r
+using Microsoft.VisualStudio.Shell.Interop;\r
+using Microsoft.VisualStudio.TextManager.Interop;\r
+using System;\r
+using System.Collections;\r
+using System.ComponentModel;\r
+using System.ComponentModel.Design;\r
+using System.IO;\r
+using System.Runtime.InteropServices;\r
+using System.Windows.Forms;\r
+using System.Xml.Linq;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ [PackageRegistration(UseManagedResourcesOnly = true)]\r
+ [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]\r
+ [ProvideMenuResource("Menus.ctmenu", 1)]\r
+ [Guid(GuidList.guidClangTidyPkgString)]\r
+ [ProvideOptionPage(typeof(ClangTidyConfigurationPage), "LLVM/Clang", "ClangTidy", 0, 0, true)]\r
+ public sealed class ClangTidyPackage : Package\r
+ {\r
+ #region Package Members\r
+ protected override void Initialize()\r
+ {\r
+ base.Initialize();\r
+\r
+ var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;\r
+ if (commandService != null)\r
+ {\r
+ var menuCommandID = new CommandID(GuidList.guidClangTidyCmdSet, (int)PkgCmdIDList.cmdidClangTidy);\r
+ var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);\r
+ commandService.AddCommand(menuItem);\r
+ }\r
+ }\r
+ #endregion\r
+\r
+ private void MenuItemCallback(object sender, EventArgs args)\r
+ {\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Reflection;\r
+using System.Runtime.InteropServices;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+\r
+ public class ClangTidyProperties : DynamicPropertyComponent\r
+ {\r
+ private static ClangTidyProperties RootProperties_ = null;\r
+ private CheckTree CheckTree_;\r
+ private bool HasUnsavedChanges_ = false;\r
+\r
+ public struct CheckMapping\r
+ {\r
+ public string CheckName;\r
+ public string Property;\r
+ }\r
+\r
+ public ClangTidyProperties()\r
+ : base(null)\r
+ {\r
+ AddClangCheckProperties();\r
+ CheckTree_ = CheckTree.Build(this);\r
+ }\r
+\r
+ public ClangTidyProperties(DynamicPropertyComponent Parent)\r
+ : base(Parent)\r
+ {\r
+ AddClangCheckProperties();\r
+ CheckTree_ = CheckTree.Build(this);\r
+ }\r
+\r
+ static ClangTidyProperties()\r
+ {\r
+ RootProperties_ = new ClangTidyProperties(null);\r
+ }\r
+\r
+ public static ClangTidyProperties RootProperties\r
+ {\r
+ get { return RootProperties_; }\r
+ }\r
+\r
+ private void AddClangCheckProperties()\r
+ {\r
+ // Add each check in the check database\r
+ HashSet<string> Categories = new HashSet<string>();\r
+ foreach (var Check in CheckDatabase.Checks)\r
+ {\r
+ string Name = Check.Name.Replace('-', '_');\r
+ List<Attribute> Attrs = new List<Attribute>();\r
+ Attrs.Add(new CategoryAttribute(Check.Category));\r
+ Attrs.Add(new DisplayNameAttribute(Check.Label));\r
+ Attrs.Add(new DefaultValueAttribute(true));\r
+ Attrs.Add(new DescriptionAttribute(Check.Desc));\r
+ Attrs.Add(new ClangTidyCheckAttribute(Check.Name));\r
+ Categories.Add(Check.Category);\r
+ AddDynamicProperty<bool>(Check.Name, Attrs.ToArray());\r
+ }\r
+\r
+ // Add a category verb for each unique category.\r
+ foreach (string Cat in Categories)\r
+ {\r
+ List<Attribute> Attrs = new List<Attribute>();\r
+ Attrs.Add(new CategoryAttribute(Cat));\r
+ Attrs.Add(new DisplayNameAttribute("(Category Verbs)"));\r
+ Attrs.Add(new TypeConverterAttribute(typeof(CategoryVerbConverter)));\r
+ Attrs.Add(new DefaultValueAttribute(CategoryVerb.None));\r
+ AddDynamicProperty<CategoryVerb>(Cat + "Verb", Attrs.ToArray());\r
+ }\r
+ }\r
+\r
+ public CheckTree GetCheckTree() { return CheckTree_; }\r
+ public bool GetHasUnsavedChanges() { return HasUnsavedChanges_; }\r
+ public void SetHasUnsavedChanges(bool Value) { HasUnsavedChanges_ = Value; }\r
+ }\r
+}\r
--- /dev/null
+namespace LLVM.ClangTidy\r
+{\r
+ partial class ClangTidyPropertyGrid\r
+ {\r
+ /// <summary> \r
+ /// Required designer variable.\r
+ /// </summary>\r
+ private System.ComponentModel.IContainer components = null;\r
+\r
+ /// <summary> \r
+ /// Clean up any resources being used.\r
+ /// </summary>\r
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>\r
+ protected override void Dispose(bool disposing)\r
+ {\r
+ if (disposing && (components != null))\r
+ {\r
+ components.Dispose();\r
+ }\r
+ base.Dispose(disposing);\r
+ }\r
+\r
+ #region Component Designer generated code\r
+\r
+ /// <summary> \r
+ /// Required method for Designer support - do not modify \r
+ /// the contents of this method with the code editor.\r
+ /// </summary>\r
+ private void InitializeComponent()\r
+ {\r
+ this.label1 = new System.Windows.Forms.Label();\r
+ this.textBox1 = new System.Windows.Forms.TextBox();\r
+ this.button1 = new System.Windows.Forms.Button();\r
+ this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();\r
+ this.clangTidyProperties1 = new LLVM.ClangTidy.ClangTidyProperties();\r
+ this.clangTidyConfigurationPage1 = new LLVM.ClangTidy.ClangTidyConfigurationPage();\r
+ this.linkLabelPath = new System.Windows.Forms.LinkLabel();\r
+ this.SuspendLayout();\r
+ // \r
+ // label1\r
+ // \r
+ this.label1.AutoSize = true;\r
+ this.label1.Location = new System.Drawing.Point(14, 17);\r
+ this.label1.Name = "label1";\r
+ this.label1.Size = new System.Drawing.Size(88, 13);\r
+ this.label1.TabIndex = 0;\r
+ this.label1.Text = "Configuration File";\r
+ // \r
+ // textBox1\r
+ // \r
+ this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) \r
+ | System.Windows.Forms.AnchorStyles.Right)));\r
+ this.textBox1.Location = new System.Drawing.Point(108, 14);\r
+ this.textBox1.Name = "textBox1";\r
+ this.textBox1.Size = new System.Drawing.Size(222, 20);\r
+ this.textBox1.TabIndex = 1;\r
+ // \r
+ // button1\r
+ // \r
+ this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));\r
+ this.button1.Location = new System.Drawing.Point(336, 14);\r
+ this.button1.Name = "button1";\r
+ this.button1.Size = new System.Drawing.Size(78, 20);\r
+ this.button1.TabIndex = 2;\r
+ this.button1.Text = "Browse";\r
+ this.button1.UseVisualStyleBackColor = true;\r
+ this.button1.Click += new System.EventHandler(this.button1_Click);\r
+ // \r
+ // propertyGrid1\r
+ // \r
+ this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) \r
+ | System.Windows.Forms.AnchorStyles.Left) \r
+ | System.Windows.Forms.AnchorStyles.Right)));\r
+ this.propertyGrid1.Location = new System.Drawing.Point(20, 73);\r
+ this.propertyGrid1.Name = "propertyGrid1";\r
+ this.propertyGrid1.SelectedObject = this.clangTidyProperties1;\r
+ this.propertyGrid1.Size = new System.Drawing.Size(391, 384);\r
+ this.propertyGrid1.TabIndex = 6;\r
+ this.propertyGrid1.ViewBorderColor = System.Drawing.SystemColors.ControlDarkDark;\r
+ this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged);\r
+ // \r
+ // linkLabelPath\r
+ // \r
+ this.linkLabelPath.AutoSize = true;\r
+ this.linkLabelPath.Location = new System.Drawing.Point(29, 50);\r
+ this.linkLabelPath.Name = "linkLabelPath";\r
+ this.linkLabelPath.Size = new System.Drawing.Size(55, 13);\r
+ this.linkLabelPath.TabIndex = 7;\r
+ this.linkLabelPath.TabStop = true;\r
+ this.linkLabelPath.Text = "linkLabel1";\r
+ this.linkLabelPath.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelPath_LinkClicked);\r
+ // \r
+ // ClangTidyPropertyGrid\r
+ // \r
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);\r
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;\r
+ this.Controls.Add(this.linkLabelPath);\r
+ this.Controls.Add(this.propertyGrid1);\r
+ this.Controls.Add(this.button1);\r
+ this.Controls.Add(this.textBox1);\r
+ this.Controls.Add(this.label1);\r
+ this.Name = "ClangTidyPropertyGrid";\r
+ this.Size = new System.Drawing.Size(444, 469);\r
+ this.ResumeLayout(false);\r
+ this.PerformLayout();\r
+\r
+ }\r
+\r
+ #endregion\r
+\r
+ private System.Windows.Forms.Label label1;\r
+ private System.Windows.Forms.TextBox textBox1;\r
+ private System.Windows.Forms.Button button1;\r
+ private System.Windows.Forms.PropertyGrid propertyGrid1;\r
+ private ClangTidyProperties clangTidyProperties1;\r
+ private ClangTidyConfigurationPage clangTidyConfigurationPage1;\r
+ private System.Windows.Forms.LinkLabel linkLabelPath;\r
+ }\r
+}\r
--- /dev/null
+//===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//\r
+//\r
+// The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+//\r
+// This class contains a UserControl consisting of a .NET PropertyGrid control\r
+// allowing configuration of checks and check options for ClangTidy.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Drawing;\r
+using System.Data;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using System.Windows.Forms;\r
+using System.IO;\r
+using Microsoft.VisualStudio.Shell;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ /// <summary>\r
+ /// A UserControl displaying a PropertyGrid allowing configuration of clang-tidy\r
+ /// checks and check options, as well as serialization and deserialization of\r
+ /// clang-tidy configuration files. When a configuration file is loaded, the\r
+ /// entire chain of configuration files is analyzed based on the file path,\r
+ /// and quick access is provided to edit or view any of the files in the\r
+ /// configuration chain, allowing easy visualization of where values come from\r
+ /// (similar in spirit to the -explain-config option of clang-tidy).\r
+ /// </summary>\r
+ public partial class ClangTidyPropertyGrid : UserControl\r
+ {\r
+ /// <summary>\r
+ /// The sequence of .clang-tidy configuration files, starting from the root\r
+ /// of the filesystem, down to the selected file.\r
+ /// </summary>\r
+ List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;\r
+\r
+ public ClangTidyPropertyGrid()\r
+ {\r
+ InitializeComponent();\r
+ InitializeSettings();\r
+ }\r
+\r
+ private enum ShouldCancel\r
+ {\r
+ Yes,\r
+ No,\r
+ }\r
+\r
+ public void SaveSettingsToStorage()\r
+ {\r
+ PersistUnsavedChanges(false);\r
+ }\r
+\r
+ private ShouldCancel PersistUnsavedChanges(bool PromptFirst)\r
+ {\r
+ var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());\r
+ if (UnsavedResults.Count() == 0)\r
+ return ShouldCancel.No;\r
+\r
+ bool ShouldSave = false;\r
+ if (PromptFirst)\r
+ {\r
+ var Response = MessageBox.Show(\r
+ "You have unsaved changes! Do you want to save before loading a new file?",\r
+ "clang-tidy",\r
+ MessageBoxButtons.YesNoCancel);\r
+\r
+ ShouldSave = (Response == DialogResult.Yes);\r
+ if (Response == DialogResult.Cancel)\r
+ return ShouldCancel.Yes;\r
+ }\r
+ else\r
+ ShouldSave = true;\r
+\r
+ if (ShouldSave)\r
+ {\r
+ foreach (var Result in UnsavedResults)\r
+ {\r
+ ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);\r
+ Result.Value.SetHasUnsavedChanges(false);\r
+ }\r
+ }\r
+ return ShouldCancel.No;\r
+ }\r
+\r
+ public void InitializeSettings()\r
+ {\r
+ PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();\r
+ PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));\r
+ reloadPropertyChain();\r
+ }\r
+\r
+ private void button1_Click(object sender, EventArgs e)\r
+ {\r
+ ShouldCancel Cancel = PersistUnsavedChanges(true);\r
+ if (Cancel == ShouldCancel.Yes)\r
+ return;\r
+\r
+ using (OpenFileDialog D = new OpenFileDialog())\r
+ {\r
+ D.Filter = "Clang Tidy files|.clang-tidy";\r
+ D.CheckPathExists = true;\r
+ D.CheckFileExists = true;\r
+\r
+ if (D.ShowDialog() == DialogResult.OK)\r
+ {\r
+ PropertyChain_.Clear();\r
+ PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);\r
+ textBox1.Text = D.FileName;\r
+ reloadPropertyChain();\r
+ }\r
+ }\r
+ }\r
+\r
+ private static readonly string DefaultText = "(Default)";\r
+ private static readonly string BrowseText = "Browse for a file to edit its properties";\r
+\r
+ /// <summary>\r
+ /// After a new configuration file is chosen, analyzes the directory hierarchy\r
+ /// and finds all .clang-tidy files in the path, parses them and updates the\r
+ /// PropertyGrid and quick-access LinkLabel control to reflect the new property\r
+ /// chain.\r
+ /// </summary>\r
+ private void reloadPropertyChain()\r
+ {\r
+ StringBuilder LinkBuilder = new StringBuilder();\r
+ LinkBuilder.Append(DefaultText);\r
+ LinkBuilder.Append(" > ");\r
+ int PrefixLength = LinkBuilder.Length;\r
+\r
+ if (PropertyChain_.Count == 1)\r
+ LinkBuilder.Append(BrowseText);\r
+ else\r
+ LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);\r
+\r
+ linkLabelPath.Text = LinkBuilder.ToString();\r
+\r
+ // Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual\r
+ // components of the path are clickable iff they contain a .clang-tidy file.\r
+ // Clicking one of the links then updates the PropertyGrid to display the\r
+ // selected .clang-tidy file.\r
+ ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;\r
+ linkLabelPath.Links.Clear();\r
+ linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);\r
+ foreach (var Prop in PropertyChain_.Skip(1))\r
+ {\r
+ LastProps = Prop.Value;\r
+ string ClangTidyFolder = Path.GetFileName(Prop.Key);\r
+ int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;\r
+ linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);\r
+ }\r
+ propertyGrid1.SelectedObject = LastProps;\r
+ }\r
+\r
+ private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)\r
+ {\r
+ ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;\r
+ Props.SetHasUnsavedChanges(true);\r
+\r
+ // When a CategoryVerb is selected, perform the corresponding action.\r
+ PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;\r
+ if (!(e.ChangedItem.Value is CategoryVerb))\r
+ return;\r
+\r
+ CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;\r
+ if (Action == CategoryVerb.None)\r
+ return;\r
+\r
+ var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();\r
+ if (Category == null)\r
+ return;\r
+ var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });\r
+ foreach (PropertyDescriptor P in SameCategoryProps)\r
+ {\r
+ if (P == Property)\r
+ continue;\r
+ switch (Action)\r
+ {\r
+ case CategoryVerb.Disable:\r
+ P.SetValue(propertyGrid1.SelectedObject, false);\r
+ break;\r
+ case CategoryVerb.Enable:\r
+ P.SetValue(propertyGrid1.SelectedObject, true);\r
+ break;\r
+ case CategoryVerb.Inherit:\r
+ P.ResetValue(propertyGrid1.SelectedObject);\r
+ break;\r
+ }\r
+ }\r
+ Property.ResetValue(propertyGrid1.SelectedObject);\r
+ propertyGrid1.Invalidate();\r
+ }\r
+\r
+ private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)\r
+ {\r
+ ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;\r
+ propertyGrid1.SelectedObject = Props;\r
+ }\r
+ }\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+ <!-- \r
+ Microsoft ResX Schema \r
+ \r
+ Version 2.0\r
+ \r
+ The primary goals of this format is to allow a simple XML format \r
+ that is mostly human readable. The generation and parsing of the \r
+ various data types are done through the TypeConverter classes \r
+ associated with the data types.\r
+ \r
+ Example:\r
+ \r
+ ... ado.net/XML headers & schema ...\r
+ <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+ <resheader name="version">2.0</resheader>\r
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+ <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+ </data>\r
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+ <comment>This is a comment</comment>\r
+ </data>\r
+ \r
+ There are any number of "resheader" rows that contain simple \r
+ name/value pairs.\r
+ \r
+ Each data row contains a name, and value. The row also contains a \r
+ type or mimetype. Type corresponds to a .NET class that support \r
+ text/value conversion through the TypeConverter architecture. \r
+ Classes that don't support this are serialized and stored with the \r
+ mimetype set.\r
+ \r
+ The mimetype is used for serialized objects, and tells the \r
+ ResXResourceReader how to depersist the object. This is currently not \r
+ extensible. For a given mimetype the value must be set accordingly:\r
+ \r
+ Note - application/x-microsoft.net.object.binary.base64 is the format \r
+ that the ResXResourceWriter will generate, however the reader can \r
+ read any of the formats listed below.\r
+ \r
+ mimetype: application/x-microsoft.net.object.binary.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+ : and then encoded with base64 encoding.\r
+ \r
+ mimetype: application/x-microsoft.net.object.soap.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+ : and then encoded with base64 encoding.\r
+\r
+ mimetype: application/x-microsoft.net.object.bytearray.base64\r
+ value : The object must be serialized into a byte array \r
+ : using a System.ComponentModel.TypeConverter\r
+ : and then encoded with base64 encoding.\r
+ -->\r
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+ <xsd:element name="root" msdata:IsDataSet="true">\r
+ <xsd:complexType>\r
+ <xsd:choice maxOccurs="unbounded">\r
+ <xsd:element name="metadata">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" use="required" type="xsd:string" />\r
+ <xsd:attribute name="type" type="xsd:string" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="assembly">\r
+ <xsd:complexType>\r
+ <xsd:attribute name="alias" type="xsd:string" />\r
+ <xsd:attribute name="name" type="xsd:string" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="data">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="resheader">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:choice>\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:schema>\r
+ <resheader name="resmimetype">\r
+ <value>text/microsoft-resx</value>\r
+ </resheader>\r
+ <resheader name="version">\r
+ <value>2.0</value>\r
+ </resheader>\r
+ <resheader name="reader">\r
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <resheader name="writer">\r
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <metadata name="clangTidyConfigurationPage1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">\r
+ <value>183, 17</value>\r
+ </metadata>\r
+</root>
\ No newline at end of file
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ partial class DynamicPropertyComponent\r
+ {\r
+ /// <summary>\r
+ /// Required designer variable.\r
+ /// </summary>\r
+ private System.ComponentModel.IContainer components = null;\r
+\r
+ /// <summary> \r
+ /// Clean up any resources being used.\r
+ /// </summary>\r
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>\r
+ protected override void Dispose(bool disposing)\r
+ {\r
+ if (disposing && (components != null))\r
+ {\r
+ components.Dispose();\r
+ }\r
+ base.Dispose(disposing);\r
+ }\r
+\r
+ #region Component Designer generated code\r
+\r
+ /// <summary>\r
+ /// Required method for Designer support - do not modify\r
+ /// the contents of this method with the code editor.\r
+ /// </summary>\r
+ private void InitializeComponent()\r
+ {\r
+ components = new System.ComponentModel.Container();\r
+ }\r
+\r
+ #endregion\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ /// <summary>\r
+ /// The goal of this class is to enable displaying of a PropertyGrid in much the\r
+ /// same way that Visual Studio's C++ project system does. A project or file can\r
+ /// have properties which might inherit from their parent, or be overridden.\r
+ /// It turns out this is somewhat non-trivial. The .NET PropertyGrid is good makes\r
+ /// displaying simple properties with a static notion of what constitutes a\r
+ /// "default" value very easy. You simply apply an Attribute to the class that says\r
+ /// what the default value is and you're done. But when you try to introduce the idea\r
+ /// that a property's default value depends on some other factor, things get much more\r
+ /// complicated due to the static nature of Attributes.\r
+ /// \r
+ /// The solution to this is to inherit from ICustomTypeDescriptor. This is the mechanism\r
+ /// by which you can inject or modify attributes or properties at runtime. The .NET\r
+ /// PropertyGrid is designed in such a way that instead of using simple .NET Reflection to\r
+ /// look for the properties and attributes on a class, it will invoke the methods of\r
+ /// ICustomTypeDescriptor (if your type inherits from it), and ask those methods. Our\r
+ /// implementation of ICustomTypeDescriptor works by waiting until the PropertyGrid requests\r
+ /// PropertyDescriptors for each of the properties, and then "decorating" them with our\r
+ /// own custom PropertyDescriptor implementation which understands the proeprty inheritance\r
+ /// model we wish to implement.\r
+ /// </summary>\r
+ public partial class DynamicPropertyComponent : Component, ICustomTypeDescriptor\r
+ {\r
+ PropertyDescriptorCollection DynamicProperties_ = new PropertyDescriptorCollection(null);\r
+ private DynamicPropertyComponent Parent_;\r
+\r
+ public DynamicPropertyComponent(DynamicPropertyComponent Parent)\r
+ {\r
+ Parent_ = Parent;\r
+ }\r
+\r
+ public DynamicPropertyComponent(DynamicPropertyComponent Parent, IContainer container)\r
+ {\r
+ Parent_ = Parent;\r
+\r
+ container.Add(this);\r
+ InitializeComponent();\r
+ }\r
+\r
+ public AttributeCollection GetAttributes()\r
+ {\r
+ return TypeDescriptor.GetAttributes(GetType());\r
+ }\r
+\r
+ public string GetClassName()\r
+ {\r
+ return TypeDescriptor.GetClassName(GetType());\r
+ }\r
+\r
+ public string GetComponentName()\r
+ {\r
+ return TypeDescriptor.GetComponentName(GetType());\r
+ }\r
+\r
+ public TypeConverter GetConverter()\r
+ {\r
+ return TypeDescriptor.GetConverter(GetType());\r
+ }\r
+\r
+ public EventDescriptor GetDefaultEvent()\r
+ {\r
+ return TypeDescriptor.GetDefaultEvent(GetType());\r
+ }\r
+\r
+ public PropertyDescriptor GetDefaultProperty()\r
+ {\r
+ return TypeDescriptor.GetDefaultProperty(GetType());\r
+ }\r
+\r
+ public object GetEditor(Type editorBaseType)\r
+ {\r
+ return TypeDescriptor.GetEditor(GetType(), editorBaseType);\r
+ }\r
+\r
+ public EventDescriptorCollection GetEvents()\r
+ {\r
+ return TypeDescriptor.GetEvents(GetType());\r
+ }\r
+\r
+ public EventDescriptorCollection GetEvents(Attribute[] attributes)\r
+ {\r
+ return TypeDescriptor.GetEvents(GetType(), attributes);\r
+ }\r
+\r
+ public PropertyDescriptorCollection GetProperties()\r
+ {\r
+ return DynamicProperties_;\r
+ }\r
+\r
+ public PropertyDescriptorCollection GetProperties(Attribute[] attributes)\r
+ {\r
+ var Props = DynamicProperties_.OfType<PropertyDescriptor>();\r
+ var Filtered = Props.Where(x => x.Attributes.Contains(attributes)).ToArray();\r
+ return new PropertyDescriptorCollection(Filtered);\r
+ }\r
+\r
+ public object GetPropertyOwner(PropertyDescriptor pd)\r
+ {\r
+ return this;\r
+ }\r
+\r
+ public void SetDynamicValue<T>(string Name, T Value)\r
+ {\r
+ Name = Name.Replace('-', '_');\r
+ DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);\r
+ Descriptor.SetValue(this, Value);\r
+ }\r
+\r
+ public T GetDynamicValue<T>(string Name)\r
+ {\r
+ Name = Name.Replace('-', '_');\r
+ DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);\r
+ return (T)Descriptor.GetValue(this);\r
+ }\r
+\r
+ protected void AddDynamicProperty<T>(string Name, Attribute[] Attributes)\r
+ {\r
+ Name = Name.Replace('-', '_');\r
+\r
+ // If we have a parent, find the corresponding PropertyDescriptor with the same\r
+ // name from the parent.\r
+ DynamicPropertyDescriptor<T> ParentDescriptor = null;\r
+ if (Parent_ != null)\r
+ ParentDescriptor = (DynamicPropertyDescriptor<T>)Parent_.GetProperties().Find(Name, false);\r
+\r
+ DynamicProperties_.Add(new DynamicPropertyDescriptor<T>(Name, ParentDescriptor, Name, Attributes));\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ class MagicInheritance\r
+ {\r
+ public static readonly string Value = "{3A27184D-1774-489B-9BB7-7191B8E8E622}";\r
+ public static readonly string Text = "<Inherit from project or parent>";\r
+ }\r
+\r
+\r
+ class DynamicPropertyConverter<T> : TypeConverter\r
+ {\r
+ private DynamicPropertyDescriptor<T> Descriptor_;\r
+ private TypeConverter Root_;\r
+\r
+ public DynamicPropertyConverter(DynamicPropertyDescriptor<T> Descriptor, TypeConverter Root)\r
+ {\r
+ Descriptor_ = Descriptor;\r
+ Root_ = Root;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Returns true if there are specific values that can be chosen from a dropdown\r
+ /// for this property. Regardless of whether standard values are supported for\r
+ /// the underlying type, we always support standard values because we need to\r
+ /// display the inheritance option.\r
+ /// </summary>\r
+ /// <returns>true</returns>\r
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext context)\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Get the set of all standard values that can be chosen from a dropdown for this\r
+ /// property. If the underlying type supports standard values, we want to include\r
+ /// all those. Additionally, we want to display the option to inherit the value,\r
+ /// but only if the value is not already inheriting.\r
+ /// </summary>\r
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)\r
+ {\r
+ List<object> Values = new List<object>();\r
+ if (Root_.GetStandardValuesSupported(context))\r
+ {\r
+ StandardValuesCollection RootValues = Root_.GetStandardValues(context);\r
+ Values.AddRange(RootValues.Cast<object>());\r
+ }\r
+ if (!Descriptor_.IsInheriting)\r
+ Values.Add(MagicInheritance.Value);\r
+ StandardValuesCollection Result = new StandardValuesCollection(Values);\r
+ return Result;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Determines whether this property can accept values other than those specified\r
+ /// in the dropdown (for example by manually typing into the field).\r
+ /// </summary>\r
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)\r
+ {\r
+ // Although we add items to the dropdown list, we do not change whether or not\r
+ // the set of values are exclusive. If the user could type into the field before\r
+ // they still can. And if they couldn't before, they still can't.\r
+ return Root_.GetStandardValuesExclusive(context);\r
+ }\r
+\r
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)\r
+ {\r
+ return Root_.CanConvertFrom(context, sourceType);\r
+ }\r
+\r
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)\r
+ {\r
+ return Root_.CanConvertTo(context, destinationType);\r
+ }\r
+\r
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)\r
+ {\r
+ if (value.Equals(MagicInheritance.Value))\r
+ return MagicInheritance.Text;\r
+ return Root_.ConvertFrom(context, culture, value);\r
+ }\r
+\r
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)\r
+ {\r
+ if (value.GetType() == destinationType)\r
+ return value;\r
+\r
+ return Root_.ConvertTo(context, culture, value, destinationType);\r
+ }\r
+\r
+ public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)\r
+ {\r
+ return Root_.CreateInstance(context, propertyValues);\r
+ }\r
+\r
+ public override bool Equals(object obj)\r
+ {\r
+ return Root_.Equals(obj);\r
+ }\r
+\r
+ public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)\r
+ {\r
+ return Root_.GetCreateInstanceSupported(context);\r
+ }\r
+\r
+ public override int GetHashCode()\r
+ {\r
+ return Root_.GetHashCode();\r
+ }\r
+\r
+ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)\r
+ {\r
+ return Root_.GetProperties(context, value, attributes);\r
+ }\r
+\r
+ public override bool GetPropertiesSupported(ITypeDescriptorContext context)\r
+ {\r
+ return Root_.GetPropertiesSupported(context);\r
+ }\r
+\r
+ public override bool IsValid(ITypeDescriptorContext context, object value)\r
+ {\r
+ return Root_.IsValid(context, value);\r
+ }\r
+\r
+ public override string ToString()\r
+ {\r
+ return Root_.ToString();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ public class DynamicPropertyDescriptor<T> : PropertyDescriptor\r
+ {\r
+ T Value_;\r
+ DynamicPropertyDescriptor<T> Parent_;\r
+ bool IsInheriting_;\r
+ object Component_;\r
+\r
+ public DynamicPropertyDescriptor(object Component, DynamicPropertyDescriptor<T> Parent, string Name, Attribute[] Attrs)\r
+ : base(Name, Attrs)\r
+ {\r
+ foreach (DefaultValueAttribute Attr in Attrs.OfType<DefaultValueAttribute>())\r
+ {\r
+ Value_ = (T)Attr.Value;\r
+ }\r
+ Parent_ = Parent;\r
+ IsInheriting_ = true;\r
+ Component_ = Component;\r
+ }\r
+\r
+ public bool IsInheriting { get { return IsInheriting_; } set { IsInheriting_ = value; } }\r
+ public DynamicPropertyDescriptor<T> Parent { get { return Parent_; } }\r
+\r
+ /// <summary>\r
+ /// Determines whether this property's value should be considered "default" (e.g.\r
+ /// displayed in bold in the property grid). Root properties are unmodifiable and\r
+ /// always default. Non-root properties are default iff they are inheriting.\r
+ /// That is to say, if a property is explicitly set to False, the property should\r
+ /// be serialized even if the parent is also False. It would only not be serialized\r
+ /// if the user had explicitly chosen to inherit it.\r
+ /// </summary>\r
+ /// <param name="component"></param>\r
+ /// <returns></returns>\r
+ public override bool ShouldSerializeValue(object component)\r
+ {\r
+ return (Parent_ != null) && !IsInheriting;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Set the value back to the default. For root properties, this essentially does\r
+ /// nothing as they are read-only anyway. For non-root properties, this only means\r
+ /// that the property is now inheriting.\r
+ /// </summary>\r
+ /// <param name="component"></param>\r
+ public override void ResetValue(object component)\r
+ {\r
+ IsInheriting_ = true;\r
+ }\r
+\r
+ public override void SetValue(object component, object value)\r
+ {\r
+ // This is a bit of a trick. If the user chose the inheritance option from the\r
+ // dropdown, we will try to set the value to that string. So look for that and\r
+ // then just reset the value.\r
+ if (value.Equals(MagicInheritance.Text))\r
+ ResetValue(component);\r
+ else\r
+ {\r
+ // By explicitly setting the value, this property is no longer inheriting,\r
+ // even if the value the property is being set to is the same as that of\r
+ // the parent.\r
+ IsInheriting_ = false;\r
+ Value_ = (T)value;\r
+ }\r
+ }\r
+\r
+ public override TypeConverter Converter\r
+ {\r
+ get\r
+ {\r
+ // We need to return a DynamicPropertyConverter<> that can deal with our requirement\r
+ // to inject the inherit property option into the dropdown. But we still need to use\r
+ // the "real" converter to do the actual work for the underlying type. Therefore,\r
+ // we need to look for a TypeConverter<> attribute on the property, and if it is present\r
+ // forward an instance of that converter to the DynamicPropertyConverter<>. Otherwise,\r
+ // forward an instance of the default converter for type T to the DynamicPropertyConverter<>.\r
+ TypeConverter UnderlyingConverter = null;\r
+ var ConverterAttr = this.Attributes.OfType<TypeConverterAttribute>().LastOrDefault();\r
+ if (ConverterAttr != null)\r
+ {\r
+ Type ConverterType = Type.GetType(ConverterAttr.ConverterTypeName);\r
+ UnderlyingConverter = (TypeConverter)Activator.CreateInstance(ConverterType);\r
+ }\r
+ else\r
+ UnderlyingConverter = TypeDescriptor.GetConverter(typeof(T));\r
+\r
+ return new DynamicPropertyConverter<T>(this, UnderlyingConverter);\r
+ }\r
+ }\r
+\r
+ public override bool IsReadOnly\r
+ {\r
+ get\r
+ {\r
+ return (Parent_ == null);\r
+ }\r
+ }\r
+\r
+ public override Type ComponentType\r
+ {\r
+ get\r
+ {\r
+ return Component_.GetType();\r
+ }\r
+ }\r
+\r
+ public override object GetValue(object component)\r
+ {\r
+ // Return either this property's value or the parents value, depending on\r
+ // whether or not this property is inheriting.\r
+ if (IsInheriting_ && Parent != null)\r
+ return Parent.GetValue(component);\r
+ return Value_;\r
+ }\r
+\r
+ public override bool CanResetValue(object component)\r
+ {\r
+ return !IsReadOnly;\r
+ }\r
+\r
+ public override Type PropertyType\r
+ {\r
+ get\r
+ {\r
+ return typeof(T);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ /// <summary>\r
+ /// A decorator of sorts. Accepts a PropertyDescriptor to its constructor\r
+ /// and forwards all calls to the underlying PropertyDescriptor. In this way\r
+ /// we can inherit from ForwardingPropertyDescriptor and override only the\r
+ /// few methods we need to customize the behavior of, while allowing the\r
+ /// underlying PropertyDescriptor to do the real work.\r
+ /// </summary>\r
+ public abstract class ForwardingPropertyDescriptor : PropertyDescriptor\r
+ {\r
+ private readonly PropertyDescriptor root;\r
+ protected PropertyDescriptor Root { get { return root; } }\r
+ protected ForwardingPropertyDescriptor(PropertyDescriptor root)\r
+ : base(root)\r
+ {\r
+ this.root = root;\r
+ }\r
+\r
+ public override void AddValueChanged(object component, EventHandler handler)\r
+ {\r
+ root.AddValueChanged(component, handler);\r
+ }\r
+\r
+ public override AttributeCollection Attributes\r
+ {\r
+ get\r
+ {\r
+ return root.Attributes;\r
+ }\r
+ }\r
+\r
+ public override bool CanResetValue(object component)\r
+ {\r
+ return root.CanResetValue(component);\r
+ }\r
+\r
+ public override string Category\r
+ {\r
+ get\r
+ {\r
+ return root.Category;\r
+ }\r
+ }\r
+\r
+ public override Type ComponentType\r
+ {\r
+ get\r
+ {\r
+ return root.ComponentType;\r
+ }\r
+ }\r
+\r
+ public override TypeConverter Converter\r
+ {\r
+ get\r
+ {\r
+ return root.Converter;\r
+ }\r
+ }\r
+\r
+ public override string Description\r
+ {\r
+ get\r
+ {\r
+ return root.Description;\r
+ }\r
+ }\r
+\r
+ public override bool DesignTimeOnly\r
+ {\r
+ get\r
+ {\r
+ return root.DesignTimeOnly;\r
+ }\r
+ }\r
+\r
+ public override string DisplayName\r
+ {\r
+ get\r
+ {\r
+ return root.DisplayName;\r
+ }\r
+ }\r
+\r
+ public override bool Equals(object obj)\r
+ {\r
+ return root.Equals(obj);\r
+ }\r
+\r
+ public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)\r
+ {\r
+ return root.GetChildProperties(instance, filter);\r
+ }\r
+\r
+ public override object GetEditor(Type editorBaseType)\r
+ {\r
+ return root.GetEditor(editorBaseType);\r
+ }\r
+\r
+ public override int GetHashCode()\r
+ {\r
+ return root.GetHashCode();\r
+ }\r
+\r
+ public override object GetValue(object component)\r
+ {\r
+ return root.GetValue(component);\r
+ }\r
+\r
+ public override bool IsBrowsable\r
+ {\r
+ get\r
+ {\r
+ return root.IsBrowsable;\r
+ }\r
+ }\r
+\r
+ public override bool IsLocalizable\r
+ {\r
+ get\r
+ {\r
+ return root.IsLocalizable;\r
+ }\r
+ }\r
+\r
+ public override bool IsReadOnly\r
+ {\r
+ get\r
+ {\r
+ return root.IsReadOnly;\r
+ }\r
+ }\r
+\r
+ public override string Name\r
+ {\r
+ get\r
+ {\r
+ return root.Name;\r
+ }\r
+ }\r
+\r
+ public override Type PropertyType\r
+ {\r
+ get\r
+ {\r
+ return root.PropertyType;\r
+ }\r
+ }\r
+\r
+ public override void RemoveValueChanged(object component, EventHandler handler)\r
+ {\r
+ root.RemoveValueChanged(component, handler);\r
+ }\r
+\r
+ public override void ResetValue(object component)\r
+ {\r
+ root.ResetValue(component);\r
+ }\r
+\r
+ public override void SetValue(object component, object value)\r
+ {\r
+ root.SetValue(component, value);\r
+ }\r
+\r
+ public override bool ShouldSerializeValue(object component)\r
+ {\r
+ return root.ShouldSerializeValue(component);\r
+ }\r
+\r
+ public override bool SupportsChangeEvents\r
+ {\r
+ get\r
+ {\r
+ return root.SupportsChangeEvents;\r
+ }\r
+ }\r
+\r
+ public override string ToString()\r
+ {\r
+ return root.ToString();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+// This file is used by Code Analysis to maintain SuppressMessage\r
+// attributes that are applied to this project. Project-level\r
+// suppressions either have no target or are given a specific target\r
+// and scoped to a namespace, type, member, etc.\r
+//\r
+// To add a suppression to this file, right-click the message in the\r
+// Error List, point to "Suppress Message(s)", and click "In Project\r
+// Suppression File". You do not need to add suppressions to this\r
+// file manually.\r
+\r
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]\r
--- /dev/null
+using System;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ static class GuidList\r
+ {\r
+ public const string guidClangTidyPkgString = "AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C";\r
+ public const string guidClangTidyCmdSetString = "9E0F0493-6493-46DE-AEE1-ACD8F60F265E";\r
+\r
+ public static readonly Guid guidClangTidyCmdSet = new Guid(guidClangTidyCmdSetString);\r
+ };\r
+}\r
--- /dev/null
+namespace LLVM.ClangTidy\r
+{\r
+ static class PkgCmdIDList\r
+ {\r
+ public const uint cmdidClangTidy = 0x100;\r
+ };\r
+}
\ No newline at end of file
--- /dev/null
+using System;\r
+using System.Reflection;\r
+using System.Resources;\r
+using System.Runtime.CompilerServices;\r
+using System.Runtime.InteropServices;\r
+\r
+// General Information about an assembly is controlled through the following \r
+// set of attributes. Change these attribute values to modify the information\r
+// associated with an assembly.\r
+[assembly: AssemblyTitle("ClangFormat")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("LLVM")]\r
+[assembly: AssemblyProduct("ClangFormat")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+[assembly: ComVisible(false)]\r
+[assembly: CLSCompliant(false)]\r
+[assembly: NeutralResourcesLanguage("en-US")]\r
+\r
+// Version information for an assembly consists of the following four values:\r
+//\r
+// Major Version\r
+// Minor Version \r
+// Build Number\r
+// Revision\r
+//\r
+// You can specify all the values or you can default the Revision and Build Numbers \r
+// by using the '*' as shown below:\r
+// FIXME: Add a way to have this generated automatically by CMake\r
+[assembly: AssemblyVersion("1.1.0.0")]\r
+[assembly: AssemblyFileVersion("1.1.0.0")]\r
--- /dev/null
+//------------------------------------------------------------------------------\r
+// <auto-generated>\r
+// This code was generated by a tool.\r
+// Runtime Version:4.0.30319.42000\r
+//\r
+// Changes to this file may cause incorrect behavior and will be lost if\r
+// the code is regenerated.\r
+// </auto-generated>\r
+//------------------------------------------------------------------------------\r
+\r
+namespace LLVM.ClangTidy {\r
+ using System;\r
+ \r
+ \r
+ /// <summary>\r
+ /// A strongly-typed resource class, for looking up localized strings, etc.\r
+ /// </summary>\r
+ // This class was auto-generated by the StronglyTypedResourceBuilder\r
+ // class via a tool like ResGen or Visual Studio.\r
+ // To add or remove a member, edit your .ResX file then rerun ResGen\r
+ // with the /str option, or rebuild your VS project.\r
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]\r
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\r
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\r
+ internal class Resources {\r
+ \r
+ private static global::System.Resources.ResourceManager resourceMan;\r
+ \r
+ private static global::System.Globalization.CultureInfo resourceCulture;\r
+ \r
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]\r
+ internal Resources() {\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Returns the cached ResourceManager instance used by this class.\r
+ /// </summary>\r
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\r
+ internal static global::System.Resources.ResourceManager ResourceManager {\r
+ get {\r
+ if (object.ReferenceEquals(resourceMan, null)) {\r
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangTidy.Resources", typeof(Resources).Assembly);\r
+ resourceMan = temp;\r
+ }\r
+ return resourceMan;\r
+ }\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Overrides the current thread's CurrentUICulture property for all\r
+ /// resource lookups using this strongly typed resource class.\r
+ /// </summary>\r
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\r
+ internal static global::System.Globalization.CultureInfo Culture {\r
+ get {\r
+ return resourceCulture;\r
+ }\r
+ set {\r
+ resourceCulture = value;\r
+ }\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Looks up a localized string similar to ---\r
+ ///Checks:\r
+ ///Checks:\r
+ /// - Name: cert-dcl54-cpp\r
+ /// Label: Overloaded allocation function pairs\r
+ /// Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope\r
+ /// Category: CERT Secure Coding Standards\r
+ /// - Name: cppcoreguidelines-interfaces-global-init\r
+ /// Label: I.22 - Complex Global Initializers\r
+ /// Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global object [rest of string was truncated]";.\r
+ /// </summary>\r
+ internal static string ClangTidyChecks {\r
+ get {\r
+ return ResourceManager.GetString("ClangTidyChecks", resourceCulture);\r
+ }\r
+ }\r
+ }\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+ <!-- \r
+ Microsoft ResX Schema \r
+ \r
+ Version 2.0\r
+ \r
+ The primary goals of this format is to allow a simple XML format \r
+ that is mostly human readable. The generation and parsing of the \r
+ various data types are done through the TypeConverter classes \r
+ associated with the data types.\r
+ \r
+ Example:\r
+ \r
+ ... ado.net/XML headers & schema ...\r
+ <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+ <resheader name="version">2.0</resheader>\r
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+ <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+ </data>\r
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+ <comment>This is a comment</comment>\r
+ </data>\r
+ \r
+ There are any number of "resheader" rows that contain simple \r
+ name/value pairs.\r
+ \r
+ Each data row contains a name, and value. The row also contains a \r
+ type or mimetype. Type corresponds to a .NET class that support \r
+ text/value conversion through the TypeConverter architecture. \r
+ Classes that don't support this are serialized and stored with the \r
+ mimetype set.\r
+ \r
+ The mimetype is used for serialized objects, and tells the \r
+ ResXResourceReader how to depersist the object. This is currently not \r
+ extensible. For a given mimetype the value must be set accordingly:\r
+ \r
+ Note - application/x-microsoft.net.object.binary.base64 is the format \r
+ that the ResXResourceWriter will generate, however the reader can \r
+ read any of the formats listed below.\r
+ \r
+ mimetype: application/x-microsoft.net.object.binary.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+ : and then encoded with base64 encoding.\r
+ \r
+ mimetype: application/x-microsoft.net.object.soap.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+ : and then encoded with base64 encoding.\r
+\r
+ mimetype: application/x-microsoft.net.object.bytearray.base64\r
+ value : The object must be serialized into a byte array \r
+ : using a System.ComponentModel.TypeConverter\r
+ : and then encoded with base64 encoding.\r
+ -->\r
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+ <xsd:element name="root" msdata:IsDataSet="true">\r
+ <xsd:complexType>\r
+ <xsd:choice maxOccurs="unbounded">\r
+ <xsd:element name="metadata">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" use="required" type="xsd:string" />\r
+ <xsd:attribute name="type" type="xsd:string" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="assembly">\r
+ <xsd:complexType>\r
+ <xsd:attribute name="alias" type="xsd:string" />\r
+ <xsd:attribute name="name" type="xsd:string" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="data">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="resheader">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:choice>\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:schema>\r
+ <resheader name="resmimetype">\r
+ <value>text/microsoft-resx</value>\r
+ </resheader>\r
+ <resheader name="version">\r
+ <value>2.0</value>\r
+ </resheader>\r
+ <resheader name="reader">\r
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <resheader name="writer">\r
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />\r
+ <data name="ClangTidyChecks" type="System.Resources.ResXFileRef, System.Windows.Forms">\r
+ <value>Resources\ClangTidyChecks.yaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>\r
+ </data>\r
+</root>
\ No newline at end of file
--- /dev/null
+---\r
+Checks:\r
+ # This file should be updated when new checks are added, and eventually we should\r
+ # generate this file automatically from the .rst files in clang-tidy.\r
+ - Category: CERT Secure Coding Standards\r
+ Label: Overloaded allocation function pairs\r
+ Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope\r
+ Name: cert-dcl54-cpp\r
+ - Category: C++ Core Guidelines\r
+ Label: I.22 - Complex Global Initializers\r
+ Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global objects\r
+ Name: cppcoreguidelines-interfaces-global-init\r
+ - Category: CERT Secure Coding Standards\r
+ Label: DCL50-CPP\r
+ Description: Checks for violations of CERT DCL50-CPP - Do not define a C-style variadic function\r
+ Name: cert-dcl50-cpp\r
+ - Category: C++ Core Guidelines\r
+ Label: Bounds.1 - No pointer arithmetic\r
+ Description: Checks for violations of Core Guideline Bounds.3 - Don't use pointer arithmetic. Use span<> instead.\r
+ Name: cppcoreguidelines-pro-bounds-pointer-arithmetic\r
+ - Category: C++ Core Guidelines\r
+ Label: Bounds.2 - Constant array indices\r
+ Description: Checks for violations of Core Bounds.2 - Only index into arrays using constant expressions.\r
+ Name: cppcoreguidelines-pro-bounds-constant-array-index\r
+ - Category: C++ Core Guidelines\r
+ Label: Bounds.3 - Array to Pointer Decay\r
+ Description: Checks for violations of Core Guideline Bounds.3 - No array-to-pointer decay\r
+ Name: cppcoreguidelines-pro-bounds-array-to-pointer-decay\r
+ - Category: C++ Core Guidelines\r
+ Label: const_cast (Type.3)\r
+ Description: Checks for violations of Core Guideline Type.3 - Don't use const_cast to cast away const\r
+ Name: cppcoreguidelines-pro-type-const-cast\r
+ - Category: C++ Core Guidelines\r
+ Label: C style casts (Type.4)\r
+ Description: Checks for violations of Core Guideline Type.3 - Don't use C-style (T)expression casts that would perform a static downcast, const_cast, or reinterpret_cast\r
+ Name: cppcoreguidelines-pro-type-cstyle-cast\r
+ - Category: C++ Core Guidelines\r
+ Label: reinterpret_cast (Type.1)\r
+ Description: Checks for violations of Core Guideline Type.1 - Don't use reinterpret_cast.\r
+ Name: cppcoreguidelines-pro-type-reinterpret-cast\r
+ - Category: C++ Core Guidelines\r
+ Label: Prefer dynamic_cast (Type.2)\r
+ Description: Checks for violations of Core Guideline Type.2 - Don't use static_cast downcasts. Use dynamic_cast instead.\r
+ Name: cppcoreguidelines-pro-type-static-cast-downcast\r
+ - Category: C++ Core Guidelines\r
+ Label: Member variable initialization (Type.6)\r
+ Description: Checks for violations of Core Guideline Type.6 - Always initialize a member variable.\r
+ Name: cppcoreguidelines-pro-type-member-init\r
+ - Category: C++ Core Guidelines\r
+ Label: Avoid unions (Type.7)\r
+ Description: Checks for violations of Core Guideline Type.7 - Avoid accessing members of raw unions. Use variant instead.\r
+ Name: cppcoreguidelines-pro-type-union-access\r
+ - Category: C++ Core Guidelines\r
+ Label: Don't use varargs (Type.8)\r
+ Description: Checks for violations of Core Guideline Type.8 - Avoid reading varargs or passing vararg arguments. Prefer variadic templates instead.\r
+ Name: cppcoreguidelines-pro-type-vararg\r
+ - Category: C++ Core Guidelines\r
+ Label: Don't slice (ES.63 & C.145)\r
+ Description: Checks for violations of Core Guidelines ES.63 (Don't slice) and C.145 (Access polymorphic objects through pointers and references)\r
+ Name: cppcoreguidelines-slicing\r
+ - Category: C++ Core Guidelines\r
+ Label: Detect unsafe special functions (C.21)\r
+ Description: Checks for violations of Core Guidelines C.21 - If you define or =delete any default operation, define or =delete them all.\r
+ Name: cppcoreguidelines-special-member-functions\r
+ - Category: Google Style Guide\r
+ Label: Forbid explicitly parameterized make_pair\r
+ Description: \r
+ Name: google-build-explicit-make-pair\r
+ - Category: Google Style Guide\r
+ Label: Anonymous namespace in headers\r
+ Description: \r
+ Name: google-build-namespaces\r
+ - Category: Google Style Guide\r
+ Label: Find using namespace directives\r
+ Description: \r
+ Name: google-build-using-namespace\r
+ - Category: Google Style Guide\r
+ Label: Default arguments in virtual methods\r
+ Description: \r
+ Name: google-default-arguments\r
+ - Category: Google Style Guide\r
+ Label: explicit constructors\r
+ Description: \r
+ Name: google-explicit-constructor\r
+ - Category: Google Style Guide\r
+ Label: Global namespace pollution in headers\r
+ Description: \r
+ Name: google-global-names-in-headers\r
+ - Category: Google Style Guide\r
+ Label: Braces around statements\r
+ Description: \r
+ Name: google-readability-braces-around-statements\r
+ - Category: Google Style Guide\r
+ Label: No C-style casts\r
+ Description: \r
+ Name: google-readability-casting\r
+ - Category: Google Style Guide\r
+ Label: Find large functions\r
+ Description: \r
+ Name: google-readability-function-size\r
+ - Category: Google Style Guide\r
+ Label: Namespace closing comments\r
+ Description: \r
+ Name: google-readability-namespace-comments\r
+ - Category: Google Style Guide\r
+ Label: Find unnecessary calls to .get()\r
+ Description: \r
+ Name: google-readability-redundant-smartptr-get\r
+ - Category: Google Style Guide\r
+ Label: Find noncomformant TODO comments\r
+ Description: \r
+ Name: google-readability-todo\r
+ - Category: Google Style Guide\r
+ Label: Find implementation-specific integral types\r
+ Description: \r
+ Name: google-runtime-int\r
+ - Category: Google Style Guide\r
+ Label: Find const string references\r
+ Description: \r
+ Name: google-runtime-member-string-references\r
+ - Category: Google Style Guide\r
+ Label: Find zero-length memsets\r
+ Description: \r
+ Name: google-runtime-memset\r
+ - Category: Google Style Guide\r
+ Label: Find overloads of operator&\r
+ Description: \r
+ Name: google-runtime-operator\r
+ - Category: Google Style Guide\r
+ Label: Check usage of non-const references\r
+ Description: \r
+ Name: google-runtime-references\r
+ - Category: LLVM Style Guide\r
+ Label: LLVM header guards\r
+ Description: \r
+ Name: llvm-header-guard\r
+ - Category: LLVM Style Guide\r
+ Label: LLVM include order\r
+ Description: \r
+ Name: llvm-include-order\r
+ - Category: LLVM Style Guide\r
+ Label: LLVM namespace comments\r
+ Description: \r
+ Name: llvm-namespace-comment\r
+ - Category: LLVM Style Guide\r
+ Label: Find local twines\r
+ Description: \r
+ Name: llvm-twine-local\r
+ - Category: Clang Diagnostics\r
+ Label: Warnings\r
+ Description: \r
+ Name: clang-diagnostic-warning\r
+ - Category: Clang Diagnostics\r
+ Label: Errors\r
+ Description: \r
+ Name: clang-diagnostic-error\r
+ - Category: Clang Diagnostics\r
+ Label: Unknown\r
+ Description: \r
+ Name: clang-diagnostic-unknown\r
+ - Category: Miscellaneous\r
+ Label: Validate argument comments\r
+ Description: \r
+ Name: misc-argument-comment\r
+ - Category: Miscellaneous\r
+ Label: Side effects in assert()\r
+ Description: \r
+ Name: misc-assert-side-effect\r
+ - Category: Miscellaneous\r
+ Label: bool / pointer implicit conversions\r
+ Description: \r
+ Name: misc-bool-pointer-implicit-conversion\r
+ - Category: Miscellaneous\r
+ Label: Dangling handles\r
+ Description: \r
+ Name: misc-dangling-handle\r
+ - Category: Miscellaneous\r
+ Label: Definitions in headers\r
+ Description: \r
+ Name: misc-definitions-in-headers\r
+ - Category: Miscellaneous\r
+ Label: Type mismatch in fold operations\r
+ Description: \r
+ Name: misc-fold-init-type\r
+ - Category: Miscellaneous\r
+ Label: Forward declaration namespace\r
+ Description: \r
+ Name: misc-forward-declaration-namespace\r
+ - Category: Miscellaneous\r
+ Label: Inaccurate erase\r
+ Description: \r
+ Name: misc-inaccurate-erase\r
+ - Category: Miscellaneous\r
+ Label: Incorrect rounding\r
+ Description: \r
+ Name: misc-incorrect-roundings\r
+ - Category: Miscellaneous\r
+ Label: Inefficient STL algorithms\r
+ Description: \r
+ Name: misc-inefficient-algorithm\r
+ - Category: Miscellaneous\r
+ Label: Macro parentheses\r
+ Description: \r
+ Name: misc-macro-parentheses\r
+ - Category: Miscellaneous\r
+ Label: Macro repeated side effects\r
+ Description: \r
+ Name: misc-macro-repeated-side-effects\r
+ - Category: Miscellaneous\r
+ Label: Misplaced const\r
+ Description: \r
+ Name: misc-misplaced-const\r
+ - Category: Miscellaneous\r
+ Label: Misplaced widening casts\r
+ Description: \r
+ Name: misc-misplaced-widening-cast\r
+ - Category: Miscellaneous\r
+ Label: Move constructor const arguments\r
+ Description: \r
+ Name: misc-move-const-arg\r
+ - Category: Miscellaneous\r
+ Label: Move constructor initialization\r
+ Description: \r
+ Name: misc-move-constructor-init\r
+ - Category: Miscellaneous\r
+ Label: Multi-statement macros\r
+ Description: \r
+ Name: misc-multiple-statement-macro\r
+ - Category: Miscellaneous\r
+ Label: Verify new / delete overloads\r
+ Description: \r
+ Name: misc-new-delete-overloads\r
+ - Category: Miscellaneous\r
+ Label: Ensure move constructors are noexcept\r
+ Description: \r
+ Name: misc-noexcept-move-constructor\r
+ - Category: Miscellaneous\r
+ Label: Copying of non-copyable objects\r
+ Description: \r
+ Name: misc-non-copyable-objects\r
+ - Category: Miscellaneous\r
+ Label: Find redundant expressions\r
+ Description: \r
+ Name: misc-redundant-expression\r
+ - Category: Miscellaneous\r
+ Label: sizeof() on stl containers\r
+ Description: \r
+ Name: misc-sizeof-container\r
+ - Category: Miscellaneous\r
+ Label: Suspicious sizeof() usage\r
+ Description: \r
+ Name: misc-sizeof-expression\r
+ - Category: Miscellaneous\r
+ Label: Replace assert with static_assert\r
+ Description: \r
+ Name: misc-static-assert\r
+ - Category: Miscellaneous\r
+ Label: Suspicious string constructor\r
+ Description: \r
+ Name: misc-string-constructor\r
+ - Category: Miscellaneous\r
+ Label: String integer assignment\r
+ Description: \r
+ Name: misc-string-integer-assignment\r
+ - Category: Miscellaneous\r
+ Label: String literal with embedded null\r
+ Description: \r
+ Name: misc-string-literal-with-embedded-nul\r
+ - Category: Miscellaneous\r
+ Label: Suspicious missing comma\r
+ Description: \r
+ Name: misc-suspicious-missing-comma\r
+ - Category: Miscellaneous\r
+ Label: Suspicious semicolon\r
+ Description: \r
+ Name: misc-suspicious-semicolon\r
+ - Category: Miscellaneous\r
+ Label: Suspicious string compare\r
+ Description: \r
+ Name: misc-suspicious-string-compare\r
+ - Category: Miscellaneous\r
+ Label: Swapped arguments\r
+ Description: \r
+ Name: misc-swapped-arguments\r
+ - Category: Miscellaneous\r
+ Label: Throw by value / catch by reference\r
+ Description: \r
+ Name: misc-throw-by-value-catch-by-reference\r
+ - Category: Miscellaneous\r
+ Label: Unconventional operator=()\r
+ Description: \r
+ Name: misc-unconventional-assign-operator\r
+ - Category: Miscellaneous\r
+ Label: Undelegated constructor\r
+ Description: \r
+ Name: misc-undelegated-constructor\r
+ - Category: Miscellaneous\r
+ Label: unique_ptr<> reset / release\r
+ Description: \r
+ Name: misc-uniqueptr-reset-release\r
+ - Category: Miscellaneous\r
+ Label: Unused Alias Decls\r
+ Description: \r
+ Name: misc-unused-alias-decls\r
+ - Category: Miscellaneous\r
+ Label: Unused Params\r
+ Description: \r
+ Name: misc-unused-parameters\r
+ - Category: Miscellaneous\r
+ Label: Unused Raii\r
+ Description: \r
+ Name: misc-unused-raii\r
+ - Category: Miscellaneous\r
+ Label: Unused Using Decls\r
+ Description: \r
+ Name: misc-unused-using-decls\r
+ - Category: Miscellaneous\r
+ Label: Virtual Near Miss\r
+ Description: \r
+ Name: misc-virtual-near-miss\r
+...\r
--- /dev/null
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+ static class Utility\r
+ {\r
+ public static IEnumerable<string> SplitPath(string FileOrDir)\r
+ {\r
+ string P = Path.GetDirectoryName(FileOrDir);\r
+ do\r
+ {\r
+ yield return P;\r
+ P = Path.GetDirectoryName(P);\r
+ } while (P != null);\r
+ }\r
+\r
+ public static bool HasClangTidyFile(string Folder)\r
+ {\r
+ string ClangTidy = Path.Combine(Folder, ".clang-tidy");\r
+ return File.Exists(ClangTidy);\r
+ }\r
+\r
+ public static bool MatchWildcardString(string Value, string Pattern)\r
+ {\r
+ string RE = Regex.Escape(Pattern).Replace(@"\*", ".*");\r
+ return Regex.IsMatch(Value, RE);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+ <!-- \r
+ Microsoft ResX Schema \r
+ \r
+ Version 2.0\r
+ \r
+ The primary goals of this format is to allow a simple XML format \r
+ that is mostly human readable. The generation and parsing of the \r
+ various data types are done through the TypeConverter classes \r
+ associated with the data types.\r
+ \r
+ Example:\r
+ \r
+ ... ado.net/XML headers & schema ...\r
+ <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+ <resheader name="version">2.0</resheader>\r
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+ <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+ </data>\r
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+ <comment>This is a comment</comment>\r
+ </data>\r
+ \r
+ There are any number of "resheader" rows that contain simple \r
+ name/value pairs.\r
+ \r
+ Each data row contains a name, and value. The row also contains a \r
+ type or mimetype. Type corresponds to a .NET class that support \r
+ text/value conversion through the TypeConverter architecture. \r
+ Classes that don't support this are serialized and stored with the \r
+ mimetype set.\r
+ \r
+ The mimetype is used for serialized objects, and tells the \r
+ ResXResourceReader how to depersist the object. This is currently not \r
+ extensible. For a given mimetype the value must be set accordingly:\r
+ \r
+ Note - application/x-microsoft.net.object.binary.base64 is the format \r
+ that the ResXResourceWriter will generate, however the reader can \r
+ read any of the formats listed below.\r
+ \r
+ mimetype: application/x-microsoft.net.object.binary.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+ : and then encoded with base64 encoding.\r
+ \r
+ mimetype: application/x-microsoft.net.object.soap.base64\r
+ value : The object must be serialized with \r
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+ : and then encoded with base64 encoding.\r
+\r
+ mimetype: application/x-microsoft.net.object.bytearray.base64\r
+ value : The object must be serialized into a byte array \r
+ : using a System.ComponentModel.TypeConverter\r
+ : and then encoded with base64 encoding.\r
+ -->\r
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+ <xsd:element name="root" msdata:IsDataSet="true">\r
+ <xsd:complexType>\r
+ <xsd:choice maxOccurs="unbounded">\r
+ <xsd:element name="metadata">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" use="required" type="xsd:string" />\r
+ <xsd:attribute name="type" type="xsd:string" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="assembly">\r
+ <xsd:complexType>\r
+ <xsd:attribute name="alias" type="xsd:string" />\r
+ <xsd:attribute name="name" type="xsd:string" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="data">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+ <xsd:attribute ref="xml:space" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ <xsd:element name="resheader">\r
+ <xsd:complexType>\r
+ <xsd:sequence>\r
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+ </xsd:sequence>\r
+ <xsd:attribute name="name" type="xsd:string" use="required" />\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:choice>\r
+ </xsd:complexType>\r
+ </xsd:element>\r
+ </xsd:schema>\r
+ <resheader name="resmimetype">\r
+ <value>text/microsoft-resx</value>\r
+ </resheader>\r
+ <resheader name="version">\r
+ <value>2.0</value>\r
+ </resheader>\r
+ <resheader name="reader">\r
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <resheader name="writer">\r
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+ </resheader>\r
+ <data name="110" xml:space="preserve">\r
+ <value>ClangTidy</value>\r
+ </data>\r
+ <data name="112" xml:space="preserve">\r
+ <value>Analyzes code by calling the clang-tidy executable.</value>\r
+ </data>\r
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />\r
+ <data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">\r
+ <value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\r
+ </data>\r
+</root>
\ No newline at end of file
--- /dev/null
+==============================================================================
+LLVM Release License
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+The LLVM software contains code written by third parties. Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
+The following pieces of software have additional or alternate copyrights,
+licenses, and/or restrictions:
+
+Program Directory
+------- ---------
+<none yet>
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<packages>\r
+ <package id="Brutal.Dev.StrongNameSigner" version="1.8.0" targetFramework="net45" />\r
+ <package id="YamlDotNet" version="3.3.0" targetFramework="net45" />\r
+ <package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net45" />\r
+</packages>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">\r
+ <Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">\r
+ <Name>ClangFormat</Name>\r
+ <Author>LLVM</Author>\r
+ <Version>4.0.0</Version>\r
+ <Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>\r
+ <Locale>1033</Locale>\r
+ <MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>\r
+ <License>license.txt</License>\r
+ <InstalledByMsi>false</InstalledByMsi>\r
+ <SupportedProducts>\r
+ <VisualStudio Version="10.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="11.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="12.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="14.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ </SupportedProducts>\r
+ <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />\r
+ </Identifier>\r
+ <References>\r
+ <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">\r
+ <Name>Visual Studio MPF</Name>\r
+ </Reference>\r
+ </References>\r
+ <Content>\r
+ <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>\r
+ </Content>\r
+</Vsix>\r
--- /dev/null
+This directory contains a VSPackage project to generate a Visual Studio extension\r
+for clang-tidy.\r
+\r
+Build prerequisites are:\r
+- Visual Studio 2013 Professional\r
+- Visual Studio 2013 SDK\r
+- Visual Studio 2010 Professional (?)\r
+- Visual Studio 2010 SDK (?)\r
+\r
+The extension is built using CMake by setting BUILD_CLANG_TIDY_VS_PLUGIN=ON\r
+when configuring a Clang build, and building the clang_tidy_vsix target.\r
+\r
+The CMake build will copy clang-tidy.exe and LICENSE.TXT into the ClangTidy/\r
+directory so they can be bundled with the plug-in, as well as creating\r
+ClangTidy/source.extension.vsixmanifest. Once the plug-in has been built with\r
+CMake once, it can be built manually from the ClangTidy.sln solution in Visual\r
+Studio.\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">\r
+ <Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">\r
+ <Name>ClangTidy</Name>\r
+ <Author>LLVM</Author>\r
+ <Version>@CLANG_TIDY_VS_VERSION@</Version>\r
+ <Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>\r
+ <Locale>1033</Locale>\r
+ <MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>\r
+ <License>license.txt</License>\r
+ <InstalledByMsi>false</InstalledByMsi>\r
+ <SupportedProducts>\r
+ <VisualStudio Version="10.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="11.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="12.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ <VisualStudio Version="14.0">\r
+ <Edition>Pro</Edition>\r
+ </VisualStudio>\r
+ </SupportedProducts>\r
+ <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />\r
+ </Identifier>\r
+ <References>\r
+ <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">\r
+ <Name>Visual Studio MPF</Name>\r
+ </Reference>\r
+ </References>\r
+ <Content>\r
+ <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>\r
+ </Content>\r
+</Vsix>\r
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangTidy
+ ClangTidy.cpp
+ ClangTidyModule.cpp
+ ClangTidyDiagnosticConsumer.cpp
+ ClangTidyOptions.cpp
+
+ DEPENDS
+ ClangSACheckers
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangRewrite
+ clangSema
+ clangStaticAnalyzerCore
+ clangStaticAnalyzerFrontend
+ clangTooling
+ clangToolingCore
+ )
+
+add_subdirectory(android)
+add_subdirectory(boost)
+add_subdirectory(bugprone)
+add_subdirectory(cert)
+add_subdirectory(cppcoreguidelines)
+add_subdirectory(google)
+add_subdirectory(hicpp)
+add_subdirectory(llvm)
+add_subdirectory(misc)
+add_subdirectory(modernize)
+add_subdirectory(mpi)
+add_subdirectory(performance)
+add_subdirectory(plugin)
+add_subdirectory(readability)
+add_subdirectory(tool)
+add_subdirectory(utils)
--- /dev/null
+//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements a clang-tidy tool.
+///
+/// This tool uses the Clang Tooling infrastructure, see
+/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+/// for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidy.h"
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyModuleRegistry.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Frontend/FixItRewriter.h"
+#include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/Tooling/DiagnosticsYaml.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include <algorithm>
+#include <utility>
+
+using namespace clang::ast_matchers;
+using namespace clang::driver;
+using namespace clang::tooling;
+using namespace llvm;
+
+LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
+
+namespace clang {
+namespace tidy {
+
+namespace {
+static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
+
+class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
+public:
+ AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
+
+ void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override {
+ for (const ento::PathDiagnostic *PD : Diags) {
+ SmallString<64> CheckName(AnalyzerCheckNamePrefix);
+ CheckName += PD->getCheckName();
+ Context.diag(CheckName, PD->getLocation().asLocation(),
+ PD->getShortDescription())
+ << PD->path.back()->getRanges();
+
+ for (const auto &DiagPiece :
+ PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
+ Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
+ DiagPiece->getString(), DiagnosticIDs::Note)
+ << DiagPiece->getRanges();
+ }
+ }
+ }
+
+ StringRef getName() const override { return "ClangTidyDiags"; }
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override { return true; }
+
+private:
+ ClangTidyContext &Context;
+};
+
+class ErrorReporter {
+public:
+ ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
+ : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
+ DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
+ Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
+ DiagPrinter),
+ SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
+ TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
+ DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
+ DiagPrinter->BeginSourceFile(LangOpts);
+ }
+
+ SourceManager &getSourceManager() { return SourceMgr; }
+
+ void reportDiagnostic(const ClangTidyError &Error) {
+ const tooling::DiagnosticMessage &Message = Error.Message;
+ SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
+ // Contains a pair for each attempted fix: location and whether the fix was
+ // applied successfully.
+ SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
+ {
+ auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
+ std::string Name = Error.DiagnosticName;
+ if (Error.IsWarningAsError) {
+ Name += ",-warnings-as-errors";
+ Level = DiagnosticsEngine::Error;
+ WarningsAsErrors++;
+ }
+ auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
+ << Message.Message << Name;
+ for (const auto &FileAndReplacements : Error.Fix) {
+ for (const auto &Repl : 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;
+ ++TotalFixes;
+ bool CanBeApplied = false;
+ if (Repl.isApplicable()) {
+ SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
+ Files.makeAbsolutePath(FixAbsoluteFilePath);
+ if (ApplyFixes) {
+ tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
+ Repl.getLength(),
+ Repl.getReplacementText());
+ Replacements &Replacements = FileReplacements[R.getFilePath()];
+ llvm::Error Err = Replacements.add(R);
+ if (Err) {
+ // FIXME: Implement better conflict handling.
+ llvm::errs() << "Trying to resolve conflict: "
+ << llvm::toString(std::move(Err)) << "\n";
+ unsigned NewOffset =
+ Replacements.getShiftedCodePosition(R.getOffset());
+ unsigned NewLength = Replacements.getShiftedCodePosition(
+ R.getOffset() + R.getLength()) -
+ NewOffset;
+ if (NewLength == R.getLength()) {
+ R = Replacement(R.getFilePath(), NewOffset, NewLength,
+ R.getReplacementText());
+ Replacements = Replacements.merge(tooling::Replacements(R));
+ CanBeApplied = true;
+ ++AppliedFixes;
+ } else {
+ llvm::errs()
+ << "Can't resolve conflict, skipping the replacement.\n";
+ }
+
+ } else {
+ CanBeApplied = true;
+ ++AppliedFixes;
+ }
+ }
+ FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
+ SourceLocation FixEndLoc =
+ FixLoc.getLocWithOffset(Repl.getLength());
+ Range = SourceRange(FixLoc, FixEndLoc);
+ Diag << FixItHint::CreateReplacement(Range,
+ Repl.getReplacementText());
+ }
+
+ if (ApplyFixes)
+ FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
+ }
+ }
+ }
+ for (auto Fix : FixLocations) {
+ Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
+ : diag::note_fixit_failed);
+ }
+ for (const auto &Note : Error.Notes)
+ reportNote(Note);
+ }
+
+ void Finish() {
+ if (ApplyFixes && TotalFixes > 0) {
+ Rewriter Rewrite(SourceMgr, LangOpts);
+ for (const auto &FileAndReplacements : FileReplacements) {
+ StringRef File = FileAndReplacements.first();
+ llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ SourceMgr.getFileManager().getBufferForFile(File);
+ if (!Buffer) {
+ llvm::errs() << "Can't get buffer for file " << File << ": "
+ << Buffer.getError().message() << "\n";
+ // FIXME: Maybe don't apply fixes for other files as well.
+ continue;
+ }
+ StringRef Code = Buffer.get()->getBuffer();
+ auto Style = format::getStyle(
+ *Context.getOptionsForFile(File).FormatStyle, File, "none");
+ if (!Style) {
+ llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+ continue;
+ }
+ llvm::Expected<tooling::Replacements> Replacements =
+ format::cleanupAroundReplacements(Code, FileAndReplacements.second,
+ *Style);
+ if (!Replacements) {
+ llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
+ continue;
+ }
+ if (llvm::Expected<tooling::Replacements> FormattedReplacements =
+ format::formatReplacements(Code, *Replacements, *Style)) {
+ Replacements = std::move(FormattedReplacements);
+ if (!Replacements)
+ llvm_unreachable("!Replacements");
+ } else {
+ llvm::errs() << llvm::toString(FormattedReplacements.takeError())
+ << ". Skipping formatting.\n";
+ }
+ if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
+ llvm::errs() << "Can't apply replacements for file " << File << "\n";
+ }
+ }
+ if (Rewrite.overwriteChangedFiles()) {
+ llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
+ } else {
+ llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
+ << TotalFixes << " suggested fixes.\n";
+ }
+ }
+ }
+
+ unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
+
+private:
+ SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
+ if (FilePath.empty())
+ return SourceLocation();
+
+ const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
+ FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
+ return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
+ }
+
+ void reportNote(const tooling::DiagnosticMessage &Message) {
+ SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
+ Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
+ << Message.Message;
+ }
+
+ FileManager Files;
+ LangOptions LangOpts; // FIXME: use langopts from each original file
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+ DiagnosticConsumer *DiagPrinter;
+ DiagnosticsEngine Diags;
+ SourceManager SourceMgr;
+ llvm::StringMap<Replacements> FileReplacements;
+ ClangTidyContext &Context;
+ bool ApplyFixes;
+ unsigned TotalFixes;
+ unsigned AppliedFixes;
+ unsigned WarningsAsErrors;
+};
+
+class ClangTidyASTConsumer : public MultiplexConsumer {
+public:
+ ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
+ std::unique_ptr<ast_matchers::MatchFinder> Finder,
+ std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
+ : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
+ Checks(std::move(Checks)) {}
+
+private:
+ std::unique_ptr<ast_matchers::MatchFinder> Finder;
+ std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+};
+
+} // namespace
+
+ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
+ ClangTidyContext &Context)
+ : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
+ for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+ E = ClangTidyModuleRegistry::end();
+ I != E; ++I) {
+ std::unique_ptr<ClangTidyModule> Module(I->instantiate());
+ Module->addCheckFactories(*CheckFactories);
+ }
+}
+
+static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
+ AnalyzerOptionsRef AnalyzerOptions) {
+ StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
+ for (const auto &Opt : Opts.CheckOptions) {
+ StringRef OptName(Opt.first);
+ if (!OptName.startswith(AnalyzerPrefix))
+ continue;
+ AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
+ }
+}
+
+typedef std::vector<std::pair<std::string, bool>> CheckersList;
+
+static CheckersList getCheckersControlList(ClangTidyContext &Context) {
+ CheckersList List;
+
+ const auto &RegisteredCheckers =
+ AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
+ bool AnalyzerChecksEnabled = false;
+ for (StringRef CheckName : RegisteredCheckers) {
+ std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
+ AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
+ }
+
+ if (!AnalyzerChecksEnabled)
+ return List;
+
+ // List all static analyzer checkers that our filter enables.
+ //
+ // Always add all core checkers if any other static analyzer check is enabled.
+ // This is currently necessary, as other path sensitive checks rely on the
+ // core checkers.
+ for (StringRef CheckName : RegisteredCheckers) {
+ std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
+
+ if (CheckName.startswith("core") ||
+ Context.isCheckEnabled(ClangTidyCheckName)) {
+ List.emplace_back(CheckName, true);
+ }
+ }
+ return List;
+}
+
+std::unique_ptr<clang::ASTConsumer>
+ClangTidyASTConsumerFactory::CreateASTConsumer(
+ clang::CompilerInstance &Compiler, StringRef File) {
+ // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
+ // modify Compiler.
+ Context.setSourceManager(&Compiler.getSourceManager());
+ Context.setCurrentFile(File);
+ Context.setASTContext(&Compiler.getASTContext());
+
+ auto WorkingDir = Compiler.getSourceManager()
+ .getFileManager()
+ .getVirtualFileSystem()
+ ->getCurrentWorkingDirectory();
+ if (WorkingDir)
+ Context.setCurrentBuildDirectory(WorkingDir.get());
+
+ std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+ CheckFactories->createChecks(&Context, Checks);
+
+ ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
+ if (auto *P = Context.getCheckProfileData())
+ FinderOptions.CheckProfiling.emplace(P->Records);
+
+ std::unique_ptr<ast_matchers::MatchFinder> Finder(
+ new ast_matchers::MatchFinder(std::move(FinderOptions)));
+
+ for (auto &Check : Checks) {
+ Check->registerMatchers(&*Finder);
+ Check->registerPPCallbacks(Compiler);
+ }
+
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ if (!Checks.empty())
+ Consumers.push_back(Finder->newASTConsumer());
+
+ AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
+ // FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
+ // to true.
+ AnalyzerOptions->Config["cfg-temporary-dtors"] =
+ Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
+
+ AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
+ if (!AnalyzerOptions->CheckersControlList.empty()) {
+ setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
+ AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
+ AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
+ AnalyzerOptions->AnalyzeNestedBlocks = true;
+ AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
+ std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
+ ento::CreateAnalysisConsumer(Compiler);
+ AnalysisConsumer->AddDiagnosticConsumer(
+ new AnalyzerDiagnosticConsumer(Context));
+ Consumers.push_back(std::move(AnalysisConsumer));
+ }
+ return llvm::make_unique<ClangTidyASTConsumer>(
+ std::move(Consumers), std::move(Finder), std::move(Checks));
+}
+
+std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
+ std::vector<std::string> CheckNames;
+ for (const auto &CheckFactory : *CheckFactories) {
+ if (Context.isCheckEnabled(CheckFactory.first))
+ CheckNames.push_back(CheckFactory.first);
+ }
+
+ for (const auto &AnalyzerCheck : getCheckersControlList(Context))
+ CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
+
+ std::sort(CheckNames.begin(), CheckNames.end());
+ return CheckNames;
+}
+
+ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
+ ClangTidyOptions::OptionMap Options;
+ std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+ CheckFactories->createChecks(&Context, Checks);
+ for (const auto &Check : Checks)
+ Check->storeOptions(Options);
+ return Options;
+}
+
+DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
+ DiagnosticIDs::Level Level) {
+ return Context->diag(CheckName, Loc, Message, Level);
+}
+
+void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
+ Context->setSourceManager(Result.SourceManager);
+ check(Result);
+}
+
+OptionsView::OptionsView(StringRef CheckName,
+ const ClangTidyOptions::OptionMap &CheckOptions)
+ : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
+
+std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
+ const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
+ if (Iter != CheckOptions.end())
+ return Iter->second;
+ return Default;
+}
+
+std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
+ StringRef Default) const {
+ auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
+ if (Iter != CheckOptions.end())
+ return Iter->second;
+ // Fallback to global setting, if present.
+ Iter = CheckOptions.find(LocalName.str());
+ if (Iter != CheckOptions.end())
+ return Iter->second;
+ return Default;
+}
+
+void OptionsView::store(ClangTidyOptions::OptionMap &Options,
+ StringRef LocalName, StringRef Value) const {
+ Options[NamePrefix + LocalName.str()] = Value;
+}
+
+void OptionsView::store(ClangTidyOptions::OptionMap &Options,
+ StringRef LocalName, int64_t Value) const {
+ store(Options, LocalName, llvm::itostr(Value));
+}
+
+std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
+ clang::tidy::ClangTidyContext Context(
+ llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
+ Options));
+ ClangTidyASTConsumerFactory Factory(Context);
+ return Factory.getCheckNames();
+}
+
+ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
+ clang::tidy::ClangTidyContext Context(
+ llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
+ Options));
+ ClangTidyASTConsumerFactory Factory(Context);
+ return Factory.getCheckOptions();
+}
+
+void runClangTidy(clang::tidy::ClangTidyContext &Context,
+ const CompilationDatabase &Compilations,
+ ArrayRef<std::string> InputFiles, ProfileData *Profile) {
+ ClangTool Tool(Compilations, InputFiles);
+
+ // Add extra arguments passed by the clang-tidy command-line.
+ ArgumentsAdjuster PerFileExtraArgumentsInserter =
+ [&Context](const CommandLineArguments &Args, StringRef Filename) {
+ ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
+ CommandLineArguments AdjustedArgs = Args;
+ if (Opts.ExtraArgsBefore) {
+ auto I = AdjustedArgs.begin();
+ if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
+ ++I; // Skip compiler binary name, if it is there.
+ AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
+ Opts.ExtraArgsBefore->end());
+ }
+ if (Opts.ExtraArgs)
+ AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
+ Opts.ExtraArgs->end());
+ return AdjustedArgs;
+ };
+
+ // Remove plugins arguments.
+ ArgumentsAdjuster PluginArgumentsRemover =
+ [](const CommandLineArguments &Args, StringRef Filename) {
+ CommandLineArguments AdjustedArgs;
+ for (size_t I = 0, E = Args.size(); I < E; ++I) {
+ if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
+ (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
+ StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
+ Args[I + 2] == "-Xclang") {
+ I += 3;
+ } else
+ AdjustedArgs.push_back(Args[I]);
+ }
+ return AdjustedArgs;
+ };
+
+ Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
+ Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
+ if (Profile)
+ Context.setCheckProfileData(Profile);
+
+ ClangTidyDiagnosticConsumer DiagConsumer(Context);
+
+ Tool.setDiagnosticConsumer(&DiagConsumer);
+
+ class ActionFactory : public FrontendActionFactory {
+ public:
+ ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
+ FrontendAction *create() override { return new Action(&ConsumerFactory); }
+
+ private:
+ class Action : public ASTFrontendAction {
+ public:
+ Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef File) override {
+ return Factory->CreateASTConsumer(Compiler, File);
+ }
+
+ private:
+ ClangTidyASTConsumerFactory *Factory;
+ };
+
+ ClangTidyASTConsumerFactory ConsumerFactory;
+ };
+
+ ActionFactory Factory(Context);
+ Tool.run(&Factory);
+}
+
+void handleErrors(ClangTidyContext &Context, bool Fix,
+ unsigned &WarningsAsErrorsCount) {
+ ErrorReporter Reporter(Context, Fix);
+ vfs::FileSystem &FileSystem =
+ *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
+ auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
+ if (!InitialWorkingDir)
+ llvm::report_fatal_error("Cannot get current working path.");
+
+ for (const ClangTidyError &Error : Context.getErrors()) {
+ if (!Error.BuildDirectory.empty()) {
+ // By default, the working directory of file system is the current
+ // clang-tidy running directory.
+ //
+ // Change the directory to the one used during the analysis.
+ FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
+ }
+ Reporter.reportDiagnostic(Error);
+ // Return to the initial directory to correctly resolve next Error.
+ FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
+ }
+ Reporter.Finish();
+ WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
+}
+
+void exportReplacements(const llvm::StringRef MainFilePath,
+ const std::vector<ClangTidyError> &Errors,
+ raw_ostream &OS) {
+ TranslationUnitDiagnostics TUD;
+ TUD.MainSourceFile = MainFilePath;
+ for (const auto &Error : Errors) {
+ tooling::Diagnostic Diag = Error;
+ TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
+ }
+
+ yaml::Output YAML(OS);
+ YAML << TUD;
+}
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ClangTidy.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_CLANGTIDY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
+
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyOptions.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace clang {
+
+class CompilerInstance;
+namespace tooling {
+class CompilationDatabase;
+}
+
+namespace tidy {
+
+/// \brief Provides access to the ``ClangTidyCheck`` options via check-local
+/// names.
+///
+/// Methods of this class prepend ``CheckName + "."`` to translate check-local
+/// option names to global option names.
+class OptionsView {
+public:
+ /// \brief Initializes the instance using \p CheckName + "." as a prefix.
+ OptionsView(StringRef CheckName,
+ const ClangTidyOptions::OptionMap &CheckOptions);
+
+ /// \brief Read a named option from the ``Context``.
+ ///
+ /// Reads the option with the check-local name \p LocalName from the
+ /// ``CheckOptions``. If the corresponding key is not present, returns
+ /// \p Default.
+ std::string get(StringRef LocalName, StringRef Default) const;
+
+ /// \brief Read a named option from the ``Context``.
+ ///
+ /// 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.
+ std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
+
+ /// \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 the
+ /// ``CheckOptions``. If the corresponding key is not present, returns
+ /// \p Default.
+ template <typename T>
+ typename std::enable_if<std::is_integral<T>::value, T>::type
+ get(StringRef LocalName, T Default) const {
+ std::string Value = get(LocalName, "");
+ T Result = Default;
+ if (!Value.empty())
+ StringRef(Value).getAsInteger(10, Result);
+ 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 T>
+ typename std::enable_if<std::is_integral<T>::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,
+ StringRef Value) const;
+
+ /// \brief Stores an option with the check-local name \p LocalName with
+ /// ``int64_t`` value \p Value to \p Options.
+ void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
+ int64_t Value) const;
+
+private:
+ std::string NamePrefix;
+ const ClangTidyOptions::OptionMap &CheckOptions;
+};
+
+/// \brief Base class for all clang-tidy checks.
+///
+/// To implement a ``ClangTidyCheck``, write a subclass and override some of the
+/// base class's methods. E.g. to implement a check that validates namespace
+/// declarations, override ``registerMatchers``:
+///
+/// ~~~{.cpp}
+/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+/// Finder->addMatcher(namespaceDecl().bind("namespace"), this);
+/// }
+/// ~~~
+///
+/// and then override ``check(const MatchResult &Result)`` to do the actual
+/// check for each match.
+///
+/// A new ``ClangTidyCheck`` instance is created per translation unit.
+///
+/// FIXME: Figure out whether carrying information from one TU to another is
+/// useful/necessary.
+class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ /// \brief Initializes the check with \p CheckName and \p Context.
+ ///
+ /// Derived classes must implement the constructor with this signature or
+ /// delegate it. If a check needs to read options, it can do this in the
+ /// constructor using the Options.get() methods below.
+ ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
+ : CheckName(CheckName), Context(Context),
+ Options(CheckName, Context->getOptions().CheckOptions) {
+ assert(Context != nullptr);
+ assert(!CheckName.empty());
+ }
+
+ /// \brief Override this to register ``PPCallbacks`` with ``Compiler``.
+ ///
+ /// This should be used for clang-tidy checks that analyze preprocessor-
+ /// dependent properties, e.g. the order of include directives.
+ virtual void registerPPCallbacks(CompilerInstance &Compiler) {}
+
+ /// \brief Override this to register AST matchers with \p Finder.
+ ///
+ /// This should be used by clang-tidy checks that analyze code properties that
+ /// dependent on AST knowledge.
+ ///
+ /// You can register as many matchers as necessary with \p Finder. Usually,
+ /// "this" will be used as callback, but you can also specify other callback
+ /// classes. Thereby, different matchers can trigger different callbacks.
+ ///
+ /// If you need to merge information between the different matchers, you can
+ /// store these as members of the derived class. However, note that all
+ /// matches occur in the order of the AST traversal.
+ virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
+
+ /// \brief ``ClangTidyChecks`` that register ASTMatchers should do the actual
+ /// work in here.
+ virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
+
+ /// \brief Add a diagnostic with the check's name.
+ DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
+ DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
+
+ /// \brief Should store all options supported by this check with their
+ /// current values or default values for options that haven't been overridden.
+ ///
+ /// The check should use ``Options.store()`` to store each option it supports
+ /// whether it has the default value or it has been overridden.
+ virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
+
+private:
+ void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ StringRef getID() const override { return CheckName; }
+ std::string CheckName;
+ ClangTidyContext *Context;
+
+protected:
+ OptionsView Options;
+ /// \brief Returns the main file name of the current translation unit.
+ StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
+ /// \brief Returns the language options from the context.
+ LangOptions getLangOpts() const { return Context->getLangOpts(); }
+};
+
+class ClangTidyCheckFactories;
+
+class ClangTidyASTConsumerFactory {
+public:
+ ClangTidyASTConsumerFactory(ClangTidyContext &Context);
+
+ /// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
+
+ /// \brief Get the list of enabled checks.
+ std::vector<std::string> getCheckNames();
+
+ /// \brief Get the union of options from all checks.
+ ClangTidyOptions::OptionMap getCheckOptions();
+
+private:
+ ClangTidyContext &Context;
+ std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
+};
+
+/// \brief Fills the list of check names that are enabled when the provided
+/// filters are applied.
+std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
+
+/// \brief Returns the effective check-specific options.
+///
+/// The method configures ClangTidy with the specified \p Options and collects
+/// effective options from all created checks. The returned set of options
+/// includes default check-specific options for all keys not overridden by \p
+/// Options.
+ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
+
+/// \brief Run a set of clang-tidy checks on a set of files.
+///
+/// \param Profile if provided, it enables check profile collection in
+/// MatchFinder, and will contain the result of the profile.
+void runClangTidy(clang::tidy::ClangTidyContext &Context,
+ const tooling::CompilationDatabase &Compilations,
+ ArrayRef<std::string> InputFiles,
+ ProfileData *Profile = nullptr);
+
+// FIXME: This interface will need to be significantly extended to be useful.
+// FIXME: Implement confidence levels for displaying/fixing errors.
+//
+/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
+/// Errors containing fixes are automatically applied and reformatted. If no
+/// clang-format configuration file is found, the given \P FormatStyle is used.
+void handleErrors(ClangTidyContext &Context, bool Fix,
+ unsigned &WarningsAsErrorsCount);
+
+/// \brief Serializes replacements into YAML and writes them to the specified
+/// output stream.
+void exportReplacements(StringRef MainFilePath,
+ const std::vector<ClangTidyError> &Errors,
+ raw_ostream &OS);
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
--- /dev/null
+//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
+/// and ClangTidyError classes.
+///
+/// This tool uses the Clang Tooling infrastructure, see
+/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+/// for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyOptions.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Frontend/DiagnosticRenderer.h"
+#include "llvm/ADT/SmallString.h"
+#include <tuple>
+#include <vector>
+using namespace clang;
+using namespace tidy;
+
+namespace {
+class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
+public:
+ ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
+ DiagnosticOptions *DiagOpts,
+ ClangTidyError &Error)
+ : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
+
+protected:
+ void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
+ DiagnosticsEngine::Level Level, StringRef Message,
+ ArrayRef<CharSourceRange> Ranges,
+ DiagOrStoredDiag Info) override {
+ // Remove check name from the message.
+ // FIXME: Remove this once there's a better way to pass check names than
+ // appending the check name to the message in ClangTidyContext::diag and
+ // using getCustomDiagID.
+ std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
+ if (Message.endswith(CheckNameInMessage))
+ Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
+
+ auto TidyMessage =
+ Loc.isValid()
+ ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
+ : tooling::DiagnosticMessage(Message);
+ if (Level == DiagnosticsEngine::Note) {
+ Error.Notes.push_back(TidyMessage);
+ return;
+ }
+ assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
+ Error.Message = TidyMessage;
+ }
+
+ void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
+ DiagnosticsEngine::Level Level,
+ ArrayRef<CharSourceRange> Ranges) override {}
+
+ void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+ SmallVectorImpl<CharSourceRange> &Ranges,
+ ArrayRef<FixItHint> Hints) override {
+ assert(Loc.isValid());
+ for (const auto &FixIt : Hints) {
+ CharSourceRange Range = FixIt.RemoveRange;
+ assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
+ "Invalid range in the fix-it hint.");
+ assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
+ "Only file locations supported in fix-it hints.");
+
+ tooling::Replacement Replacement(Loc.getManager(), Range,
+ FixIt.CodeToInsert);
+ llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
+ // FIXME: better error handling (at least, don't let other replacements be
+ // applied).
+ if (Err) {
+ llvm::errs() << "Fix conflicts with existing fix! "
+ << llvm::toString(std::move(Err)) << "\n";
+ assert(false && "Fix conflicts with existing fix!");
+ }
+ }
+ }
+
+ void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
+
+ void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+ StringRef ModuleName) override {}
+
+ void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+ StringRef ModuleName) override {}
+
+ void endDiagnostic(DiagOrStoredDiag D,
+ DiagnosticsEngine::Level Level) override {
+ assert(!Error.Message.Message.empty() && "Message has not been set");
+ }
+
+private:
+ ClangTidyError &Error;
+};
+} // end anonymous namespace
+
+ClangTidyError::ClangTidyError(StringRef CheckName,
+ ClangTidyError::Level DiagLevel,
+ StringRef BuildDirectory, bool IsWarningAsError)
+ : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
+ IsWarningAsError(IsWarningAsError) {}
+
+// Returns true if GlobList starts with the negative indicator ('-'), removes it
+// from the GlobList.
+static bool ConsumeNegativeIndicator(StringRef &GlobList) {
+ GlobList = GlobList.trim(' ');
+ if (GlobList.startswith("-")) {
+ GlobList = GlobList.substr(1);
+ return true;
+ }
+ return false;
+}
+// Converts first glob from the comma-separated list of globs to Regex and
+// removes it and the trailing comma from the GlobList.
+static llvm::Regex ConsumeGlob(StringRef &GlobList) {
+ StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
+ StringRef Glob = UntrimmedGlob.trim(' ');
+ GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
+ SmallString<128> RegexText("^");
+ StringRef MetaChars("()^$|*+?.[]\\{}");
+ for (char C : Glob) {
+ if (C == '*')
+ RegexText.push_back('.');
+ else if (MetaChars.find(C) != StringRef::npos)
+ RegexText.push_back('\\');
+ RegexText.push_back(C);
+ }
+ RegexText.push_back('$');
+ return llvm::Regex(RegexText);
+}
+
+GlobList::GlobList(StringRef Globs)
+ : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
+ NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
+
+bool GlobList::contains(StringRef S, bool Contains) {
+ if (Regex.match(S))
+ Contains = Positive;
+
+ if (NextGlob)
+ Contains = NextGlob->contains(S, Contains);
+ return Contains;
+}
+
+class ClangTidyContext::CachedGlobList {
+public:
+ CachedGlobList(StringRef Globs) : Globs(Globs) {}
+
+ bool contains(StringRef S) {
+ switch (auto &Result = Cache[S]) {
+ case Yes: return true;
+ case No: return false;
+ case None:
+ Result = Globs.contains(S) ? Yes : No;
+ return Result == Yes;
+ }
+ llvm_unreachable("invalid enum");
+ }
+
+private:
+ GlobList Globs;
+ enum Tristate { None, Yes, No };
+ llvm::StringMap<Tristate> Cache;
+};
+
+ClangTidyContext::ClangTidyContext(
+ std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
+ : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
+ Profile(nullptr) {
+ // Before the first translation unit we can get errors related to command-line
+ // parsing, use empty string for the file name in this case.
+ setCurrentFile("");
+}
+
+ClangTidyContext::~ClangTidyContext() = default;
+
+DiagnosticBuilder ClangTidyContext::diag(
+ StringRef CheckName, SourceLocation Loc, StringRef Description,
+ DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
+ assert(Loc.isValid());
+ unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
+ Level, (Description + " [" + CheckName + "]").str());
+ CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
+ return DiagEngine->Report(Loc, ID);
+}
+
+void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
+ DiagEngine = Engine;
+}
+
+void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
+ DiagEngine->setSourceManager(SourceMgr);
+}
+
+void ClangTidyContext::setCurrentFile(StringRef File) {
+ CurrentFile = File;
+ CurrentOptions = getOptionsForFile(CurrentFile);
+ CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
+ WarningAsErrorFilter =
+ llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
+}
+
+void ClangTidyContext::setASTContext(ASTContext *Context) {
+ DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
+ LangOpts = Context->getLangOpts();
+}
+
+const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
+ return OptionsProvider->getGlobalOptions();
+}
+
+const ClangTidyOptions &ClangTidyContext::getOptions() const {
+ return CurrentOptions;
+}
+
+ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
+ // Merge options on top of getDefaults() as a safeguard against options with
+ // unset values.
+ return ClangTidyOptions::getDefaults().mergeWith(
+ OptionsProvider->getOptions(File));
+}
+
+void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
+
+bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
+ assert(CheckFilter != nullptr);
+ return CheckFilter->contains(CheckName);
+}
+
+bool ClangTidyContext::treatAsError(StringRef CheckName) const {
+ assert(WarningAsErrorFilter != nullptr);
+ return WarningAsErrorFilter->contains(CheckName);
+}
+
+/// \brief Store a \c ClangTidyError.
+void ClangTidyContext::storeError(const ClangTidyError &Error) {
+ Errors.push_back(Error);
+}
+
+StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
+ llvm::DenseMap<unsigned, std::string>::const_iterator I =
+ CheckNamesByDiagnosticID.find(DiagnosticID);
+ if (I != CheckNamesByDiagnosticID.end())
+ return I->second;
+ return "";
+}
+
+ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
+ ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
+ : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
+ LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
+ LastErrorWasIgnored(false) {
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ Diags = llvm::make_unique<DiagnosticsEngine>(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
+ /*ShouldOwnClient=*/false);
+ Context.setDiagnosticsEngine(Diags.get());
+}
+
+void ClangTidyDiagnosticConsumer::finalizeLastError() {
+ if (!Errors.empty()) {
+ ClangTidyError &Error = Errors.back();
+ if (!Context.isCheckEnabled(Error.DiagnosticName) &&
+ Error.DiagLevel != ClangTidyError::Error) {
+ ++Context.Stats.ErrorsIgnoredCheckFilter;
+ Errors.pop_back();
+ } else if (!LastErrorRelatesToUserCode) {
+ ++Context.Stats.ErrorsIgnoredNonUserCode;
+ Errors.pop_back();
+ } else if (!LastErrorPassesLineFilter) {
+ ++Context.Stats.ErrorsIgnoredLineFilter;
+ Errors.pop_back();
+ } else {
+ ++Context.Stats.ErrorsDisplayed;
+ }
+ }
+ LastErrorRelatesToUserCode = false;
+ LastErrorPassesLineFilter = false;
+}
+
+static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
+ bool Invalid;
+ const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
+ if (Invalid)
+ return false;
+
+ // Check if there's a NOLINT on this line.
+ const char *P = CharacterData;
+ while (*P != '\0' && *P != '\r' && *P != '\n')
+ ++P;
+ StringRef RestOfLine(CharacterData, P - CharacterData + 1);
+ // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
+ if (RestOfLine.find("NOLINT") != StringRef::npos)
+ return true;
+
+ // Check if there's a NOLINTNEXTLINE on the previous line.
+ const char *BufBegin =
+ SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
+ if (Invalid || P == BufBegin)
+ return false;
+
+ // Scan backwards over the current line.
+ P = CharacterData;
+ while (P != BufBegin && *P != '\n')
+ --P;
+
+ // If we reached the begin of the file there is no line before it.
+ if (P == BufBegin)
+ return false;
+
+ // Skip over the newline.
+ --P;
+ const char *LineEnd = P;
+
+ // Now we're on the previous line. Skip to the beginning of it.
+ while (P != BufBegin && *P != '\n')
+ --P;
+
+ RestOfLine = StringRef(P, LineEnd - P + 1);
+ if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)
+ return true;
+
+ return false;
+}
+
+static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
+ SourceLocation Loc) {
+ while (true) {
+ if (LineIsMarkedWithNOLINT(SM, Loc))
+ return true;
+ if (!Loc.isMacroID())
+ return false;
+ Loc = SM.getImmediateExpansionRange(Loc).first;
+ }
+ return false;
+}
+
+void ClangTidyDiagnosticConsumer::HandleDiagnostic(
+ DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
+ if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
+ return;
+
+ if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
+ DiagLevel != DiagnosticsEngine::Fatal &&
+ LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
+ Info.getLocation())) {
+ ++Context.Stats.ErrorsIgnoredNOLINT;
+ // Ignored a warning, should ignore related notes as well
+ LastErrorWasIgnored = true;
+ return;
+ }
+
+ LastErrorWasIgnored = false;
+ // Count warnings/errors.
+ DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
+
+ if (DiagLevel == DiagnosticsEngine::Note) {
+ assert(!Errors.empty() &&
+ "A diagnostic note can only be appended to a message.");
+ } else {
+ finalizeLastError();
+ StringRef WarningOption =
+ Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
+ Info.getID());
+ std::string CheckName = !WarningOption.empty()
+ ? ("clang-diagnostic-" + WarningOption).str()
+ : Context.getCheckName(Info.getID()).str();
+
+ if (CheckName.empty()) {
+ // This is a compiler diagnostic without a warning option. Assign check
+ // name based on its level.
+ switch (DiagLevel) {
+ case DiagnosticsEngine::Error:
+ case DiagnosticsEngine::Fatal:
+ CheckName = "clang-diagnostic-error";
+ break;
+ case DiagnosticsEngine::Warning:
+ CheckName = "clang-diagnostic-warning";
+ break;
+ default:
+ CheckName = "clang-diagnostic-unknown";
+ break;
+ }
+ }
+
+ ClangTidyError::Level Level = ClangTidyError::Warning;
+ if (DiagLevel == DiagnosticsEngine::Error ||
+ DiagLevel == DiagnosticsEngine::Fatal) {
+ // Force reporting of Clang errors regardless of filters and non-user
+ // code.
+ Level = ClangTidyError::Error;
+ LastErrorRelatesToUserCode = true;
+ LastErrorPassesLineFilter = true;
+ }
+ bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
+ Context.treatAsError(CheckName);
+ Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
+ IsWarningAsError);
+ }
+
+ ClangTidyDiagnosticRenderer Converter(
+ Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
+ Errors.back());
+ SmallString<100> Message;
+ Info.FormatDiagnostic(Message);
+ FullSourceLoc Loc =
+ (Info.getLocation().isInvalid())
+ ? FullSourceLoc()
+ : FullSourceLoc(Info.getLocation(), Info.getSourceManager());
+ Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
+ Info.getFixItHints());
+
+ checkFilters(Info.getLocation());
+}
+
+bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
+ unsigned LineNumber) const {
+ if (Context.getGlobalOptions().LineFilter.empty())
+ return true;
+ for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
+ if (FileName.endswith(Filter.Name)) {
+ if (Filter.LineRanges.empty())
+ return true;
+ for (const FileFilter::LineRange &Range : Filter.LineRanges) {
+ if (Range.first <= LineNumber && LineNumber <= Range.second)
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
+ // Invalid location may mean a diagnostic in a command line, don't skip these.
+ if (!Location.isValid()) {
+ LastErrorRelatesToUserCode = true;
+ LastErrorPassesLineFilter = true;
+ return;
+ }
+
+ const SourceManager &Sources = Diags->getSourceManager();
+ if (!*Context.getOptions().SystemHeaders &&
+ Sources.isInSystemHeader(Location))
+ return;
+
+ // FIXME: We start with a conservative approach here, but the actual type of
+ // location needed depends on the check (in particular, where this check wants
+ // to apply fixes).
+ FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
+ const FileEntry *File = Sources.getFileEntryForID(FID);
+
+ // -DMACRO definitions on the command line have locations in a virtual buffer
+ // that doesn't have a FileEntry. Don't skip these as well.
+ if (!File) {
+ LastErrorRelatesToUserCode = true;
+ LastErrorPassesLineFilter = true;
+ return;
+ }
+
+ StringRef FileName(File->getName());
+ LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
+ Sources.isInMainFile(Location) ||
+ getHeaderFilter()->match(FileName);
+
+ unsigned LineNumber = Sources.getExpansionLineNumber(Location);
+ LastErrorPassesLineFilter =
+ LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
+}
+
+llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
+ if (!HeaderFilter)
+ HeaderFilter =
+ llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
+ return HeaderFilter.get();
+}
+
+void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
+ SmallVectorImpl<ClangTidyError> &Errors) const {
+ // Each error is modelled as the set of intervals in which it applies
+ // 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
+ // 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.
+ struct Event {
+ // An event can be either the begin or the end of an interval.
+ enum EventType {
+ ET_Begin = 1,
+ ET_End = -1,
+ };
+
+ Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
+ unsigned ErrorSize)
+ : Type(Type), ErrorId(ErrorId) {
+ // The events are going to be sorted by their position. In case of draw:
+ //
+ // * If an interval ends at the same position at which other interval
+ // begins, this is not an overlapping, so we want to remove the ending
+ // interval before adding the starting one: end events have higher
+ // priority than begin events.
+ //
+ // * If we have several begin points at the same position, we will mark as
+ // 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
+ // earliest begin point. In both cases, we sort non-increasingly by the
+ // 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 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 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);
+ else
+ Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
+ }
+
+ bool operator<(const Event &Other) const {
+ return Priority < Other.Priority;
+ }
+
+ // Determines if this event is the begin or the end of an interval.
+ EventType Type;
+ // The index of the error to which the interval that generated this event
+ // belongs.
+ unsigned ErrorId;
+ // The events will be sorted based on this field.
+ std::tuple<unsigned, EventType, int, int, unsigned> Priority;
+ };
+
+ // Compute error sizes.
+ std::vector<int> Sizes;
+ for (const auto &Error : Errors) {
+ int Size = 0;
+ 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<std::string, std::vector<Event>> FileEvents;
+ for (unsigned I = 0; I < Errors.size(); ++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;
+ auto &Events = FileEvents[FilePath];
+ Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
+ Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
+ }
+ }
+ }
+
+ std::vector<bool> Apply(Errors.size(), true);
+ for (auto &FileAndEvents : FileEvents) {
+ std::vector<Event> &Events = FileAndEvents.second;
+ // Sweep.
+ std::sort(Events.begin(), Events.end());
+ int OpenIntervals = 0;
+ for (const auto &Event : Events) {
+ if (Event.Type == Event::ET_End)
+ --OpenIntervals;
+ // This has to be checked after removing the interval from the count if it
+ // is an end event, or before adding it if it is a begin event.
+ if (OpenIntervals != 0)
+ Apply[Event.ErrorId] = false;
+ if (Event.Type == Event::ET_Begin)
+ ++OpenIntervals;
+ }
+ assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
+ }
+
+ for (unsigned I = 0; I < Errors.size(); ++I) {
+ if (!Apply[I]) {
+ Errors[I].Fix.clear();
+ Errors[I].Notes.emplace_back(
+ "this fix will not be applied because it overlaps with another fix");
+ }
+ }
+}
+
+namespace {
+struct LessClangTidyError {
+ bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
+ const tooling::DiagnosticMessage &M1 = LHS.Message;
+ const tooling::DiagnosticMessage &M2 = RHS.Message;
+
+ return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
+ std::tie(M2.FilePath, M2.FileOffset, M2.Message);
+ }
+};
+struct EqualClangTidyError {
+ bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
+ LessClangTidyError Less;
+ return !Less(LHS, RHS) && !Less(RHS, LHS);
+ }
+};
+} // end anonymous namespace
+
+// Flushes the internal diagnostics buffer to the ClangTidyContext.
+void ClangTidyDiagnosticConsumer::finish() {
+ finalizeLastError();
+
+ std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
+ Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
+ Errors.end());
+
+ if (RemoveIncompatibleErrors)
+ removeIncompatibleErrors(Errors);
+
+ for (const ClangTidyError &Error : Errors)
+ Context.storeError(Error);
+ Errors.clear();
+}
--- /dev/null
+//===--- ClangTidyDiagnosticConsumer.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_CLANGTIDYDIAGNOSTICCONSUMER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
+
+#include "ClangTidyOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Diagnostic.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/Timer.h"
+
+namespace clang {
+
+class ASTContext;
+class CompilerInstance;
+namespace ast_matchers {
+class MatchFinder;
+}
+namespace tooling {
+class CompilationDatabase;
+}
+
+namespace tidy {
+
+/// \brief A detected error complete with information to display diagnostic and
+/// automatic fix.
+///
+/// This is used as an intermediate format to transport Diagnostics without a
+/// dependency on a SourceManager.
+///
+/// FIXME: Make Diagnostics flexible enough to support this directly.
+struct ClangTidyError : tooling::Diagnostic {
+ ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
+ bool IsWarningAsError);
+
+ bool IsWarningAsError;
+};
+
+/// \brief Read-only set of strings represented as a list of positive and
+/// negative globs. Positive globs add all matched strings to the set, negative
+/// globs remove them in the order of appearance in the list.
+class GlobList {
+public:
+ /// \brief \p GlobList is a comma-separated list of globs (only '*'
+ /// metacharacter is supported) with optional '-' prefix to denote exclusion.
+ GlobList(StringRef Globs);
+
+ /// \brief Returns \c true if the pattern matches \p S. The result is the last
+ /// matching glob's Positive flag.
+ bool contains(StringRef S) { return contains(S, false); }
+
+private:
+ bool contains(StringRef S, bool Contains);
+
+ bool Positive;
+ llvm::Regex Regex;
+ std::unique_ptr<GlobList> NextGlob;
+};
+
+/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
+/// run.
+struct ClangTidyStats {
+ ClangTidyStats()
+ : ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
+ ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {}
+
+ unsigned ErrorsDisplayed;
+ unsigned ErrorsIgnoredCheckFilter;
+ unsigned ErrorsIgnoredNOLINT;
+ unsigned ErrorsIgnoredNonUserCode;
+ unsigned ErrorsIgnoredLineFilter;
+
+ unsigned errorsIgnored() const {
+ return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
+ ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
+ }
+};
+
+/// \brief Container for clang-tidy profiling data.
+struct ProfileData {
+ llvm::StringMap<llvm::TimeRecord> Records;
+};
+
+/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
+/// provided by this context.
+///
+/// A \c ClangTidyCheck always has access to the active context to report
+/// warnings like:
+/// \code
+/// Context->Diag(Loc, "Single-argument constructors must be explicit")
+/// << FixItHint::CreateInsertion(Loc, "explicit ");
+/// \endcode
+class ClangTidyContext {
+public:
+ /// \brief Initializes \c ClangTidyContext instance.
+ ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
+
+ ~ClangTidyContext();
+
+ /// \brief Report any errors detected using this method.
+ ///
+ /// This is still under heavy development and will likely change towards using
+ /// tablegen'd diagnostic IDs.
+ /// FIXME: Figure out a way to manage ID spaces.
+ DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
+ StringRef Message,
+ DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
+
+ /// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
+ ///
+ /// This is called from the \c ClangTidyCheck base class.
+ void setSourceManager(SourceManager *SourceMgr);
+
+ /// \brief Should be called when starting to process new translation unit.
+ void setCurrentFile(StringRef File);
+
+ /// \brief Returns the main file name of the current translation unit.
+ StringRef getCurrentFile() const { return CurrentFile; }
+
+ /// \brief Sets ASTContext for the current translation unit.
+ void setASTContext(ASTContext *Context);
+
+ /// \brief Gets the language options from the AST context.
+ const LangOptions &getLangOpts() const { return LangOpts; }
+
+ /// \brief Returns the name of the clang-tidy check which produced this
+ /// diagnostic ID.
+ StringRef getCheckName(unsigned DiagnosticID) const;
+
+ /// \brief Returns \c true if the check is enabled for the \c CurrentFile.
+ ///
+ /// The \c CurrentFile can be changed using \c setCurrentFile.
+ bool isCheckEnabled(StringRef CheckName) const;
+
+ /// \brief Returns \c true if the check should be upgraded to error for the
+ /// \c CurrentFile.
+ bool treatAsError(StringRef CheckName) const;
+
+ /// \brief Returns global options.
+ const ClangTidyGlobalOptions &getGlobalOptions() const;
+
+ /// \brief Returns options for \c CurrentFile.
+ ///
+ /// The \c CurrentFile can be changed using \c setCurrentFile.
+ const ClangTidyOptions &getOptions() const;
+
+ /// \brief Returns options for \c File. Does not change or depend on
+ /// \c CurrentFile.
+ ClangTidyOptions getOptionsForFile(StringRef File) const;
+
+ /// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
+ /// counters.
+ const ClangTidyStats &getStats() const { return Stats; }
+
+ /// \brief Returns all collected errors.
+ ArrayRef<ClangTidyError> getErrors() const { return Errors; }
+
+ /// \brief Clears collected errors.
+ void clearErrors() { Errors.clear(); }
+
+ /// \brief Set the output struct for profile data.
+ ///
+ /// Setting a non-null pointer here will enable profile collection in
+ /// clang-tidy.
+ void setCheckProfileData(ProfileData *Profile);
+ ProfileData *getCheckProfileData() const { return Profile; }
+
+ /// \brief Should be called when starting to process new translation unit.
+ void setCurrentBuildDirectory(StringRef BuildDirectory) {
+ CurrentBuildDirectory = BuildDirectory;
+ }
+
+ /// \brief Returns build directory of the current translation unit.
+ const std::string &getCurrentBuildDirectory() {
+ return CurrentBuildDirectory;
+ }
+
+private:
+ // Calls setDiagnosticsEngine() and storeError().
+ friend class ClangTidyDiagnosticConsumer;
+ friend class ClangTidyPluginAction;
+
+ /// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
+ /// correctly.
+ void setDiagnosticsEngine(DiagnosticsEngine *Engine);
+
+ /// \brief Store an \p Error.
+ void storeError(const ClangTidyError &Error);
+
+ std::vector<ClangTidyError> Errors;
+ DiagnosticsEngine *DiagEngine;
+ std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
+
+ std::string CurrentFile;
+ ClangTidyOptions CurrentOptions;
+ class CachedGlobList;
+ std::unique_ptr<CachedGlobList> CheckFilter;
+ std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
+
+ LangOptions LangOpts;
+
+ ClangTidyStats Stats;
+
+ std::string CurrentBuildDirectory;
+
+ llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
+
+ ProfileData *Profile;
+};
+
+/// \brief A diagnostic consumer that turns each \c Diagnostic into a
+/// \c SourceManager-independent \c ClangTidyError.
+//
+// FIXME: If we move away from unit-tests, this can be moved to a private
+// implementation file.
+class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
+public:
+ ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
+ bool RemoveIncompatibleErrors = true);
+
+ // FIXME: The concept of converting between FixItHints and Replacements is
+ // more generic and should be pulled out into a more useful Diagnostics
+ // library.
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override;
+
+ /// \brief Flushes the internal diagnostics buffer to the ClangTidyContext.
+ void finish() override;
+
+private:
+ void finalizeLastError();
+
+ void removeIncompatibleErrors(SmallVectorImpl<ClangTidyError> &Errors) const;
+
+ /// \brief Returns the \c HeaderFilter constructed for the options set in the
+ /// context.
+ llvm::Regex *getHeaderFilter();
+
+ /// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
+ /// according to the diagnostic \p Location.
+ void checkFilters(SourceLocation Location);
+ bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
+
+ ClangTidyContext &Context;
+ bool RemoveIncompatibleErrors;
+ std::unique_ptr<DiagnosticsEngine> Diags;
+ SmallVector<ClangTidyError, 8> Errors;
+ std::unique_ptr<llvm::Regex> HeaderFilter;
+ bool LastErrorRelatesToUserCode;
+ bool LastErrorPassesLineFilter;
+ bool LastErrorWasIgnored;
+};
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
--- /dev/null
+//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements classes required to build clang-tidy modules.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyModule.h"
+
+namespace clang {
+namespace tidy {
+
+void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
+ CheckFactory Factory) {
+ Factories[Name] = std::move(Factory);
+}
+
+void ClangTidyCheckFactories::createChecks(
+ ClangTidyContext *Context,
+ std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
+ for (const auto &Factory : Factories) {
+ if (Context->isCheckEnabled(Factory.first))
+ Checks.emplace_back(Factory.second(Factory.first, Context));
+ }
+}
+
+ClangTidyOptions ClangTidyModule::getModuleOptions() {
+ return ClangTidyOptions();
+}
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ClangTidyModule.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_CLANGTIDYMODULE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
+
+#include "ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <utility>
+
+namespace clang {
+namespace tidy {
+
+/// \brief A collection of \c ClangTidyCheckFactory instances.
+///
+/// All clang-tidy modules register their check factories with an instance of
+/// this object.
+class ClangTidyCheckFactories {
+public:
+ typedef std::function<ClangTidyCheck *(StringRef Name,
+ ClangTidyContext *Context)>
+ CheckFactory;
+
+ /// \brief Registers check \p Factory with name \p Name.
+ ///
+ /// For all checks that have default constructors, use \c registerCheck.
+ void registerCheckFactory(StringRef Name, CheckFactory Factory);
+
+ /// \brief Registers the \c CheckType with the name \p Name.
+ ///
+ /// This method should be used for all \c ClangTidyChecks that don't require
+ /// constructor parameters.
+ ///
+ /// For example, if have a clang-tidy check like:
+ /// \code
+ /// class MyTidyCheck : public ClangTidyCheck {
+ /// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+ /// ..
+ /// }
+ /// };
+ /// \endcode
+ /// you can register it with:
+ /// \code
+ /// class MyModule : public ClangTidyModule {
+ /// void addCheckFactories(ClangTidyCheckFactories &Factories) override {
+ /// Factories.registerCheck<MyTidyCheck>("myproject-my-check");
+ /// }
+ /// };
+ /// \endcode
+ template <typename CheckType> void registerCheck(StringRef CheckName) {
+ registerCheckFactory(CheckName,
+ [](StringRef Name, ClangTidyContext *Context) {
+ return new CheckType(Name, Context);
+ });
+ }
+
+ /// \brief Create instances of all checks matching \p CheckRegexString and
+ /// store them in \p Checks.
+ ///
+ /// The caller takes ownership of the return \c ClangTidyChecks.
+ void createChecks(ClangTidyContext *Context,
+ std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
+
+ typedef std::map<std::string, CheckFactory> FactoryMap;
+ FactoryMap::const_iterator begin() const { return Factories.begin(); }
+ FactoryMap::const_iterator end() const { return Factories.end(); }
+ bool empty() const { return Factories.empty(); }
+
+private:
+ FactoryMap Factories;
+};
+
+/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
+/// them a prefixed name.
+class ClangTidyModule {
+public:
+ virtual ~ClangTidyModule() {}
+
+ /// \brief Implement this function in order to register all \c CheckFactories
+ /// belonging to this module.
+ virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
+
+ /// \brief Gets default options for checks defined in this module.
+ virtual ClangTidyOptions getModuleOptions();
+};
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
--- /dev/null
+//===--- ClangTidyModuleRegistry.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_CLANGTIDYMODULEREGISTRY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
+
+#include "ClangTidyModule.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang {
+namespace tidy {
+
+typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry;
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
--- /dev/null
+//===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyOptions.h"
+#include "ClangTidyModuleRegistry.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+#define DEBUG_TYPE "clang-tidy-options"
+
+using clang::tidy::ClangTidyOptions;
+using clang::tidy::FileFilter;
+using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
+
+namespace llvm {
+namespace yaml {
+
+// Map std::pair<int, int> to a JSON array of size 2.
+template <> struct SequenceTraits<FileFilter::LineRange> {
+ static size_t size(IO &IO, FileFilter::LineRange &Range) {
+ return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
+ }
+ static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
+ if (Index > 1)
+ IO.setError("Too many elements in line range.");
+ return Index == 0 ? Range.first : Range.second;
+ }
+};
+
+template <> struct MappingTraits<FileFilter> {
+ static void mapping(IO &IO, FileFilter &File) {
+ IO.mapRequired("name", File.Name);
+ IO.mapOptional("lines", File.LineRanges);
+ }
+ static StringRef validate(IO &io, FileFilter &File) {
+ if (File.Name.empty())
+ return "No file name specified";
+ for (const FileFilter::LineRange &Range : File.LineRanges) {
+ if (Range.first <= 0 || Range.second <= 0)
+ return "Invalid line range";
+ }
+ return StringRef();
+ }
+};
+
+template <> struct MappingTraits<ClangTidyOptions::StringPair> {
+ static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
+ IO.mapRequired("key", KeyValue.first);
+ IO.mapRequired("value", KeyValue.second);
+ }
+};
+
+struct NOptionMap {
+ NOptionMap(IO &) {}
+ NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
+ : Options(OptionMap.begin(), OptionMap.end()) {}
+ ClangTidyOptions::OptionMap denormalize(IO &) {
+ ClangTidyOptions::OptionMap Map;
+ for (const auto &KeyValue : Options)
+ Map[KeyValue.first] = KeyValue.second;
+ return Map;
+ }
+ std::vector<ClangTidyOptions::StringPair> Options;
+};
+
+template <> struct MappingTraits<ClangTidyOptions> {
+ static void mapping(IO &IO, ClangTidyOptions &Options) {
+ MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
+ IO, Options.CheckOptions);
+ IO.mapOptional("Checks", Options.Checks);
+ IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
+ IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
+ IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
+ IO.mapOptional("FormatStyle", Options.FormatStyle);
+ IO.mapOptional("User", Options.User);
+ IO.mapOptional("CheckOptions", NOpts->Options);
+ IO.mapOptional("ExtraArgs", Options.ExtraArgs);
+ IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
+ }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace tidy {
+
+ClangTidyOptions ClangTidyOptions::getDefaults() {
+ ClangTidyOptions Options;
+ Options.Checks = "";
+ Options.WarningsAsErrors = "";
+ Options.HeaderFilterRegex = "";
+ Options.SystemHeaders = false;
+ Options.AnalyzeTemporaryDtors = false;
+ Options.FormatStyle = "none";
+ Options.User = llvm::None;
+ for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+ E = ClangTidyModuleRegistry::end();
+ I != E; ++I)
+ Options = Options.mergeWith(I->instantiate()->getModuleOptions());
+ return Options;
+}
+
+template <typename T>
+static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
+ if (Src) {
+ if (Dest)
+ Dest->insert(Dest->end(), Src->begin(), Src->end());
+ else
+ Dest = Src;
+ }
+}
+
+static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
+ const Optional<std::string> &Src) {
+ if (Src)
+ Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
+}
+
+template <typename T>
+static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
+ if (Src)
+ Dest = Src;
+}
+
+ClangTidyOptions
+ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
+ ClangTidyOptions Result = *this;
+
+ mergeCommaSeparatedLists(Result.Checks, Other.Checks);
+ mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
+ overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
+ overrideValue(Result.SystemHeaders, Other.SystemHeaders);
+ overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
+ overrideValue(Result.FormatStyle, Other.FormatStyle);
+ overrideValue(Result.User, Other.User);
+ mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
+ mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
+
+ for (const auto &KeyValue : Other.CheckOptions)
+ Result.CheckOptions[KeyValue.first] = KeyValue.second;
+
+ return Result;
+}
+
+const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
+ "clang-tidy binary";
+const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
+ "command-line option '-checks'";
+const char
+ ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
+ "command-line option '-config'";
+
+ClangTidyOptions
+ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
+ ClangTidyOptions Result;
+ for (const auto &Source : getRawOptions(FileName))
+ Result = Result.mergeWith(Source.first);
+ return Result;
+}
+
+std::vector<OptionsSource>
+DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
+ std::vector<OptionsSource> Result;
+ Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
+ return Result;
+}
+
+ConfigOptionsProvider::ConfigOptionsProvider(
+ const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &ConfigOptions,
+ const ClangTidyOptions &OverrideOptions)
+ : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+ ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
+
+std::vector<OptionsSource>
+ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
+ std::vector<OptionsSource> RawOptions =
+ DefaultOptionsProvider::getRawOptions(FileName);
+ RawOptions.emplace_back(ConfigOptions,
+ OptionsSourceTypeConfigCommandLineOption);
+ RawOptions.emplace_back(OverrideOptions,
+ OptionsSourceTypeCheckCommandLineOption);
+ return RawOptions;
+}
+
+FileOptionsProvider::FileOptionsProvider(
+ const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &OverrideOptions)
+ : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+ OverrideOptions(OverrideOptions) {
+ ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
+}
+
+FileOptionsProvider::FileOptionsProvider(
+ const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &OverrideOptions,
+ const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
+ : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+ OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
+
+// FIXME: This method has some common logic with clang::format::getStyle().
+// Consider pulling out common bits to a findParentFileWithName function or
+// similar.
+std::vector<OptionsSource>
+FileOptionsProvider::getRawOptions(StringRef FileName) {
+ DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
+
+ std::vector<OptionsSource> RawOptions =
+ DefaultOptionsProvider::getRawOptions(FileName);
+ OptionsSource CommandLineOptions(OverrideOptions,
+ OptionsSourceTypeCheckCommandLineOption);
+ // Look for a suitable configuration file in all parent directories of the
+ // file. Start with the immediate parent directory and move up.
+ StringRef Path = llvm::sys::path::parent_path(FileName);
+ for (StringRef CurrentPath = Path; !CurrentPath.empty();
+ CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
+ llvm::Optional<OptionsSource> Result;
+
+ auto Iter = CachedOptions.find(CurrentPath);
+ if (Iter != CachedOptions.end())
+ Result = Iter->second;
+
+ if (!Result)
+ Result = tryReadConfigFile(CurrentPath);
+
+ if (Result) {
+ // Store cached value for all intermediate directories.
+ while (Path != CurrentPath) {
+ DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
+ << ".\n");
+ CachedOptions[Path] = *Result;
+ Path = llvm::sys::path::parent_path(Path);
+ }
+ CachedOptions[Path] = *Result;
+
+ RawOptions.push_back(*Result);
+ break;
+ }
+ }
+ RawOptions.push_back(CommandLineOptions);
+ return RawOptions;
+}
+
+llvm::Optional<OptionsSource>
+FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
+ assert(!Directory.empty());
+
+ if (!llvm::sys::fs::is_directory(Directory)) {
+ llvm::errs() << "Error reading configuration from " << Directory
+ << ": directory doesn't exist.\n";
+ return llvm::None;
+ }
+
+ for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
+ SmallString<128> ConfigFile(Directory);
+ llvm::sys::path::append(ConfigFile, ConfigHandler.first);
+ DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
+
+ bool IsFile = false;
+ // Ignore errors from is_regular_file: we only need to know if we can read
+ // the file or not.
+ llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
+ if (!IsFile)
+ continue;
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
+ llvm::MemoryBuffer::getFile(ConfigFile.c_str());
+ if (std::error_code EC = Text.getError()) {
+ llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
+ << "\n";
+ continue;
+ }
+
+ // Skip empty files, e.g. files opened for writing via shell output
+ // redirection.
+ if ((*Text)->getBuffer().empty())
+ continue;
+ llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
+ ConfigHandler.second((*Text)->getBuffer());
+ if (!ParsedOptions) {
+ if (ParsedOptions.getError())
+ llvm::errs() << "Error parsing " << ConfigFile << ": "
+ << ParsedOptions.getError().message() << "\n";
+ continue;
+ }
+ return OptionsSource(*ParsedOptions, ConfigFile.c_str());
+ }
+ return llvm::None;
+}
+
+/// \brief Parses -line-filter option and stores it to the \c Options.
+std::error_code parseLineFilter(StringRef LineFilter,
+ clang::tidy::ClangTidyGlobalOptions &Options) {
+ llvm::yaml::Input Input(LineFilter);
+ Input >> Options.LineFilter;
+ return Input.error();
+}
+
+llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
+ llvm::yaml::Input Input(Config);
+ ClangTidyOptions Options;
+ Input >> Options;
+ if (Input.error())
+ return Input.error();
+ return Options;
+}
+
+std::string configurationAsText(const ClangTidyOptions &Options) {
+ std::string Text;
+ llvm::raw_string_ostream Stream(Text);
+ llvm::yaml::Output Output(Stream);
+ // We use the same mapping method for input and output, so we need a non-const
+ // reference here.
+ ClangTidyOptions NonConstValue = Options;
+ Output << NonConstValue;
+ return Stream.str();
+}
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ClangTidyOptions.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_CLANGTIDYOPTIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorOr.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+
+/// \brief Contains a list of line ranges in a single file.
+struct FileFilter {
+ /// \brief File name.
+ std::string Name;
+
+ /// \brief LineRange is a pair<start, end> (inclusive).
+ typedef std::pair<unsigned, unsigned> LineRange;
+
+ /// \brief A list of line ranges in this file, for which we show warnings.
+ std::vector<LineRange> LineRanges;
+};
+
+/// \brief Global options. These options are neither stored nor read from
+/// configuration files.
+struct ClangTidyGlobalOptions {
+ /// \brief Output warnings from certain line ranges of certain files only.
+ /// If empty, no warnings will be filtered.
+ std::vector<FileFilter> LineFilter;
+};
+
+/// \brief Contains options for clang-tidy. These options may be read from
+/// configuration files, and may be different for different translation units.
+struct ClangTidyOptions {
+ /// \brief These options are used for all settings that haven't been
+ /// overridden by the \c OptionsProvider.
+ ///
+ /// Allow no checks and no headers by default. This method initializes
+ /// check-specific options by calling \c ClangTidyModule::getModuleOptions()
+ /// of each registered \c ClangTidyModule.
+ static ClangTidyOptions getDefaults();
+
+ /// \brief Creates a new \c ClangTidyOptions instance combined from all fields
+ /// of this instance overridden by the fields of \p Other that have a value.
+ ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const;
+
+ /// \brief Checks filter.
+ llvm::Optional<std::string> Checks;
+
+ /// \brief WarningsAsErrors filter.
+ llvm::Optional<std::string> WarningsAsErrors;
+
+ /// \brief Output warnings from headers matching this filter. Warnings from
+ /// main files will always be displayed.
+ llvm::Optional<std::string> HeaderFilterRegex;
+
+ /// \brief Output warnings from system headers matching \c HeaderFilterRegex.
+ llvm::Optional<bool> SystemHeaders;
+
+ /// \brief Turns on temporary destructor-based analysis.
+ llvm::Optional<bool> AnalyzeTemporaryDtors;
+
+ /// \brief Format code around applied fixes with clang-format using this
+ /// style.
+ ///
+ /// Can be one of:
+ /// * 'none' - don't format code around applied fixes;
+ /// * 'llvm', 'google', 'mozilla' or other predefined clang-format style
+ /// names;
+ /// * 'file' - use the .clang-format file in the closest parent directory of
+ /// each source file;
+ /// * '{inline-formatting-style-in-yaml-format}'.
+ ///
+ /// See clang-format documentation for more about configuring format style.
+ llvm::Optional<std::string> FormatStyle;
+
+ /// \brief Specifies the name or e-mail of the user running clang-tidy.
+ ///
+ /// This option is used, for example, to place the correct user name in TODO()
+ /// comments in the relevant check.
+ llvm::Optional<std::string> User;
+
+ typedef std::pair<std::string, std::string> StringPair;
+ typedef std::map<std::string, std::string> OptionMap;
+
+ /// \brief Key-value mapping used to store check-specific options.
+ OptionMap CheckOptions;
+
+ typedef std::vector<std::string> ArgList;
+
+ /// \brief Add extra compilation arguments to the end of the list.
+ llvm::Optional<ArgList> ExtraArgs;
+
+ /// \brief Add extra compilation arguments to the start of the list.
+ llvm::Optional<ArgList> ExtraArgsBefore;
+};
+
+/// \brief Abstract interface for retrieving various ClangTidy options.
+class ClangTidyOptionsProvider {
+public:
+ static const char OptionsSourceTypeDefaultBinary[];
+ static const char OptionsSourceTypeCheckCommandLineOption[];
+ static const char OptionsSourceTypeConfigCommandLineOption[];
+
+ virtual ~ClangTidyOptionsProvider() {}
+
+ /// \brief Returns global options, which are independent of the file.
+ virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0;
+
+ /// \brief ClangTidyOptions and its source.
+ //
+ /// clang-tidy has 3 types of the sources in order of increasing priority:
+ /// * clang-tidy binary.
+ /// * '-config' commandline option or a specific configuration file. If the
+ /// commandline option is specified, clang-tidy will ignore the
+ /// configuration file.
+ /// * '-checks' commandline option.
+ typedef std::pair<ClangTidyOptions, std::string> OptionsSource;
+
+ /// \brief Returns an ordered vector of OptionsSources, in order of increasing
+ /// priority.
+ virtual std::vector<OptionsSource>
+ getRawOptions(llvm::StringRef FileName) = 0;
+
+ /// \brief Returns options applying to a specific translation unit with the
+ /// specified \p FileName.
+ ClangTidyOptions getOptions(llvm::StringRef FileName);
+};
+
+/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
+/// returns the same options for all files.
+class DefaultOptionsProvider : public ClangTidyOptionsProvider {
+public:
+ DefaultOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &Options)
+ : GlobalOptions(GlobalOptions), DefaultOptions(Options) {}
+ const ClangTidyGlobalOptions &getGlobalOptions() override {
+ return GlobalOptions;
+ }
+ std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+private:
+ ClangTidyGlobalOptions GlobalOptions;
+ ClangTidyOptions DefaultOptions;
+};
+
+/// \brief Implementation of ClangTidyOptions interface, which is used for
+/// '-config' command-line option.
+class ConfigOptionsProvider : public DefaultOptionsProvider {
+public:
+ ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &ConfigOptions,
+ const ClangTidyOptions &OverrideOptions);
+ std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+private:
+ ClangTidyOptions ConfigOptions;
+ ClangTidyOptions OverrideOptions;
+};
+
+/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
+/// tries to find a configuration file in the closest parent directory of each
+/// source file.
+///
+/// By default, files named ".clang-tidy" will be considered, and the
+/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
+/// custom set of configuration file names and parsing functions can be
+/// specified using the appropriate constructor.
+class FileOptionsProvider : public DefaultOptionsProvider {
+public:
+ // \brief A pair of configuration file base name and a function parsing
+ // configuration from text in the corresponding format.
+ typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
+ llvm::StringRef)>>
+ ConfigFileHandler;
+
+ /// \brief Configuration file handlers listed in the order of priority.
+ ///
+ /// Custom configuration file formats can be supported by constructing the
+ /// list of handlers and passing it to the appropriate \c FileOptionsProvider
+ /// constructor. E.g. initialization of a \c FileOptionsProvider with support
+ /// of a custom configuration file format for files named ".my-tidy-config"
+ /// could look similar to this:
+ /// \code
+ /// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
+ /// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
+ /// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
+ /// return llvm::make_unique<FileOptionsProvider>(
+ /// GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
+ /// \endcode
+ ///
+ /// With the order of handlers shown above, the ".my-tidy-config" file would
+ /// take precedence over ".clang-tidy" if both reside in the same directory.
+ typedef std::vector<ConfigFileHandler> ConfigFileHandlers;
+
+ /// \brief Initializes the \c FileOptionsProvider instance.
+ ///
+ /// \param GlobalOptions are just stored and returned to the caller of
+ /// \c getGlobalOptions.
+ ///
+ /// \param DefaultOptions are used for all settings not specified in a
+ /// configuration file.
+ ///
+ /// If any of the \param OverrideOptions fields are set, they will override
+ /// whatever options are read from the configuration file.
+ FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &OverrideOptions);
+
+ /// \brief Initializes the \c FileOptionsProvider instance with a custom set
+ /// of configuration file handlers.
+ ///
+ /// \param GlobalOptions are just stored and returned to the caller of
+ /// \c getGlobalOptions.
+ ///
+ /// \param DefaultOptions are used for all settings not specified in a
+ /// configuration file.
+ ///
+ /// If any of the \param OverrideOptions fields are set, they will override
+ /// whatever options are read from the configuration file.
+ ///
+ /// \param ConfigHandlers specifies a custom set of configuration file
+ /// handlers. Each handler is a pair of configuration file name and a function
+ /// that can parse configuration from this file type. The configuration files
+ /// in each directory are searched for in the order of appearance in
+ /// \p ConfigHandlers.
+ FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+ const ClangTidyOptions &DefaultOptions,
+ const ClangTidyOptions &OverrideOptions,
+ const ConfigFileHandlers &ConfigHandlers);
+
+ std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+protected:
+ /// \brief Try to read configuration files from \p Directory using registered
+ /// \c ConfigHandlers.
+ llvm::Optional<OptionsSource> tryReadConfigFile(llvm::StringRef Directory);
+
+ llvm::StringMap<OptionsSource> CachedOptions;
+ ClangTidyOptions OverrideOptions;
+ ConfigFileHandlers ConfigHandlers;
+};
+
+/// \brief Parses LineFilter from JSON and stores it to the \p Options.
+std::error_code parseLineFilter(llvm::StringRef LineFilter,
+ ClangTidyGlobalOptions &Options);
+
+/// \brief Parses configuration from JSON and returns \c ClangTidyOptions or an
+/// error.
+llvm::ErrorOr<ClangTidyOptions> parseConfiguration(llvm::StringRef Config);
+
+/// \brief Serializes configuration to a YAML-encoded string.
+std::string configurationAsText(const ClangTidyOptions &Options);
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
--- /dev/null
+#!/usr/bin/env python
+#
+#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+import os
+import re
+import sys
+
+
+# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
+# and 'False' if the entry already existed.
+def adapt_cmake(module_path, check_name_camel):
+ filename = os.path.join(module_path, 'CMakeLists.txt')
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+
+ cpp_file = check_name_camel + '.cpp'
+
+ # Figure out whether this check already exists.
+ for line in lines:
+ if line.strip() == cpp_file:
+ return False
+
+ print('Updating %s...' % filename)
+ with open(filename, 'wb') as f:
+ cpp_found = False
+ file_added = False
+ for line in lines:
+ cpp_line = line.strip().endswith('.cpp')
+ if (not file_added) and (cpp_line or cpp_found):
+ cpp_found = True
+ if (line.strip() > cpp_file) or (not cpp_line):
+ f.write(' ' + cpp_file + '\n')
+ file_added = True
+ f.write(line)
+
+ return True
+
+
+# Adds a header for the new check.
+def write_header(module_path, module, check_name, check_name_camel):
+ check_name_dashes = module + '-' + check_name
+ filename = os.path.join(module_path, check_name_camel) + '.h'
+ print('Creating %s...' % filename)
+ with open(filename, 'wb') as f:
+ header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
+ + check_name.upper().replace('-', '_') + '_H')
+ f.write('//===--- ')
+ f.write(os.path.basename(filename))
+ f.write(' - clang-tidy')
+ f.write('-' * max(0, 43 - len(os.path.basename(filename))))
+ f.write('*- C++ -*-===//')
+ f.write("""
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef %(header_guard)s
+#define %(header_guard)s
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace %(module)s {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html
+class %(check_name)s : public ClangTidyCheck {
+public:
+ %(check_name)s(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace %(module)s
+} // namespace tidy
+} // namespace clang
+
+#endif // %(header_guard)s
+""" % {'header_guard': header_guard,
+ 'check_name': check_name_camel,
+ 'check_name_dashes': check_name_dashes,
+ 'module': module})
+
+
+# Adds the implementation of the new check.
+def write_implementation(module_path, module, check_name_camel):
+ filename = os.path.join(module_path, check_name_camel) + '.cpp'
+ print('Creating %s...' % filename)
+ with open(filename, 'wb') as f:
+ f.write('//===--- ')
+ f.write(os.path.basename(filename))
+ f.write(' - clang-tidy')
+ f.write('-' * max(0, 52 - len(os.path.basename(filename))))
+ f.write('-===//')
+ f.write("""
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "%(check_name)s.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace %(module)s {
+
+void %(check_name)s::registerMatchers(MatchFinder *Finder) {
+ // FIXME: Add matchers.
+ Finder->addMatcher(functionDecl().bind("x"), this);
+}
+
+void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
+ // FIXME: Add callback implementation.
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
+ if (MatchedDecl->getName().startswith("awesome_"))
+ return;
+ diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
+ << MatchedDecl
+ << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
+}
+
+} // namespace %(module)s
+} // namespace tidy
+} // namespace clang
+""" % {'check_name': check_name_camel,
+ 'module': module})
+
+
+# Modifies the module to include the new check.
+def adapt_module(module_path, module, check_name, check_name_camel):
+ modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
+ os.listdir(module_path))[0]
+ filename = os.path.join(module_path, modulecpp)
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+
+ print('Updating %s...' % filename)
+ with open(filename, 'wb') as f:
+ header_added = False
+ header_found = False
+ check_added = False
+ check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
+ '>(\n "' + module + '-' + check_name + '");\n')
+
+ for line in lines:
+ if not header_added:
+ match = re.search('#include "(.*)"', line)
+ if match:
+ header_found = True
+ if match.group(1) > check_name_camel:
+ header_added = True
+ f.write('#include "' + check_name_camel + '.h"\n')
+ elif header_found:
+ header_added = True
+ f.write('#include "' + check_name_camel + '.h"\n')
+
+ if not check_added:
+ if line.strip() == '}':
+ check_added = True
+ f.write(check_decl)
+ else:
+ match = re.search('registerCheck<(.*)>', line)
+ if match and match.group(1) > check_name_camel:
+ check_added = True
+ f.write(check_decl)
+ f.write(line)
+
+
+# Adds a release notes entry.
+def add_release_notes(module_path, module, check_name):
+ check_name_dashes = module + '-' + check_name
+ filename = os.path.normpath(os.path.join(module_path,
+ '../../docs/ReleaseNotes.rst'))
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+
+ print('Updating %s...' % filename)
+ with open(filename, 'wb') as f:
+ note_added = False
+ header_found = False
+
+ for line in lines:
+ if not note_added:
+ match = re.search('Improvements to clang-tidy', line)
+ if match:
+ header_found = True
+ elif header_found:
+ if not line.startswith('----'):
+ f.write("""
+- New `%s
+ <http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_ check
+
+ FIXME: add release notes.
+""" % (check_name_dashes, check_name_dashes))
+ note_added = True
+
+ f.write(line)
+
+
+# Adds a test for the check.
+def write_test(module_path, module, check_name):
+ check_name_dashes = module + '-' + check_name
+ filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
+ check_name_dashes + '.cpp'))
+ print('Creating %s...' % filename)
+ with open(filename, 'wb') as f:
+ f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
+
+// FIXME: Add something that triggers the check here.
+void f();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
+
+// FIXME: Verify the applied fix.
+// * Make the CHECK patterns specific enough and try to make verified lines
+// unique to avoid incorrect matches.
+// * Use {{}} for regular expressions.
+// CHECK-FIXES: {{^}}void awesome_f();{{$}}
+
+// FIXME: Add something that doesn't trigger the check here.
+void awesome_f2();
+""" % {'check_name_dashes': check_name_dashes})
+
+
+# Recreates the list of checks in the docs/clang-tidy/checks directory.
+def update_checks_list(clang_tidy_path):
+ docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
+ filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+ doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
+ os.listdir(docs_dir))
+ doc_files.sort()
+
+ def format_link(doc_file):
+ check_name = doc_file.replace('.rst', '')
+ with open(os.path.join(docs_dir, doc_file), 'r') as doc:
+ content = doc.read()
+ match = re.search('.*:orphan:.*', content)
+ if match:
+ return ''
+
+ match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
+ content)
+ if match:
+ return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
+ 'check': check_name,
+ 'target': match.group(1)
+ }
+ return ' %s\n' % check_name
+
+ checks = map(format_link, doc_files)
+
+ print('Updating %s...' % filename)
+ with open(filename, 'wb') as f:
+ for line in lines:
+ f.write(line)
+ if line.startswith('.. toctree::'):
+ f.writelines(checks)
+ break
+
+
+# Adds a documentation for the check.
+def write_docs(module_path, module, check_name):
+ check_name_dashes = module + '-' + check_name
+ filename = os.path.normpath(os.path.join(
+ module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
+ print('Creating %s...' % filename)
+ with open(filename, 'wb') as f:
+ f.write(""".. title:: clang-tidy - %(check_name_dashes)s
+
+%(check_name_dashes)s
+%(underline)s
+
+FIXME: Describe what patterns does the check detect and why. Give examples.
+""" % {'check_name_dashes': check_name_dashes,
+ 'underline': '=' * len(check_name_dashes)})
+
+
+def main():
+ if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
+ update_checks_list(os.path.dirname(sys.argv[0]))
+ return
+
+ if len(sys.argv) != 3:
+ print """\
+Usage: add_new_check.py <module> <check>, e.g.
+ add_new_check.py misc awesome-functions
+
+Alternatively, run 'add_new_check.py --update-docs' to just update the list of
+documentation files."""
+
+ return
+
+ module = sys.argv[1]
+ check_name = sys.argv[2]
+
+ if check_name.startswith(module):
+ print 'Check name "%s" must not start with the module "%s". Exiting.' % (
+ check_name, module)
+ return
+ check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
+ check_name.split('-'))) + 'Check'
+ clang_tidy_path = os.path.dirname(sys.argv[0])
+ module_path = os.path.join(clang_tidy_path, module)
+
+ if not adapt_cmake(module_path, check_name_camel):
+ return
+ write_header(module_path, module, check_name, check_name_camel)
+ write_implementation(module_path, module, check_name_camel)
+ adapt_module(module_path, module, check_name, check_name_camel)
+ add_release_notes(module_path, module, check_name)
+ write_test(module_path, module, check_name)
+ write_docs(module_path, module, check_name)
+ update_checks_list(clang_tidy_path)
+ print('Done. Now it\'s your turn!')
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+//===--- AndroidTidyModule.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 "CloexecCreatCheck.h"
+#include "CloexecFopenCheck.h"
+#include "CloexecOpenCheck.h"
+#include "CloexecSocketCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// This module is for Android specific checks.
+class AndroidModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<CloexecCreatCheck>("android-cloexec-creat");
+ CheckFactories.registerCheck<CloexecFopenCheck>("android-cloexec-fopen");
+ CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
+ CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
+ }
+};
+
+// Register the AndroidTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<AndroidModule>
+ X("android-module", "Adds Android platform checks.");
+
+} // namespace android
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the AndroidModule.
+volatile int AndroidModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyAndroidModule
+ AndroidTidyModule.cpp
+ CloexecCreatCheck.cpp
+ CloexecFopenCheck.cpp
+ CloexecOpenCheck.cpp
+ CloexecSocketCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ )
--- /dev/null
+//===--- CloexecCreatCheck.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 "CloexecCreatCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+void CloexecCreatCheck::registerMatchers(MatchFinder *Finder) {
+ auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+ auto MODETType = hasType(namedDecl(hasName("mode_t")));
+
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+ hasName("creat"),
+ hasParameter(0, CharPointerType),
+ hasParameter(1, MODETType))
+ .bind("funcDecl")))
+ .bind("creatFn"),
+ this);
+}
+
+void CloexecCreatCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("creatFn");
+ const SourceManager &SM = *Result.SourceManager;
+
+ const std::string &ReplacementText =
+ (Twine("open (") +
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ MatchedCall->getArg(0)->getSourceRange()),
+ SM, Result.Context->getLangOpts()) +
+ ", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, " +
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ MatchedCall->getArg(1)->getSourceRange()),
+ SM, Result.Context->getLangOpts()) +
+ ")")
+ .str();
+
+ diag(MatchedCall->getLocStart(),
+ "prefer open() to creat() because open() allows O_CLOEXEC")
+ << FixItHint::CreateReplacement(MatchedCall->getSourceRange(),
+ ReplacementText);
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- CloexecCreatCheck.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_ANDROID_CLOEXEC_CREAT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// creat() is better to be replaced by open().
+/// Find the usage of creat() and redirect user to use open().
+
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html
+class CloexecCreatCheck : public ClangTidyCheck {
+public:
+ CloexecCreatCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
--- /dev/null
+//===--- CloexecFopenCheck.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 "CloexecFopenCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+namespace {
+static const char MODE = 'e';
+
+// Build the replace text. If it's string constant, add 'e' directly in the end
+// of the string. Else, add "e".
+std::string BuildReplaceText(const Expr *Arg, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Arg->getLocStart().isMacroID())
+ return (Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
+ LangOpts) +
+ " \"" + Twine(MODE) + "\"")
+ .str();
+
+ StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
+ return ("\"" + SR + Twine(MODE) + "\"").str();
+}
+} // namespace
+
+void CloexecFopenCheck::registerMatchers(MatchFinder *Finder) {
+ auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isExternC(), returns(asString("FILE *")),
+ hasName("fopen"),
+ hasParameter(0, CharPointerType),
+ hasParameter(1, CharPointerType))
+ .bind("funcDecl")))
+ .bind("fopenFn"),
+ this);
+}
+
+void CloexecFopenCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("fopenFn");
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+ const Expr *ModeArg = MatchedCall->getArg(1);
+
+ // Check if the 'e' may be in the mode string.
+ const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
+ if (!ModeStr || (ModeStr->getString().find(MODE) != StringRef::npos))
+ return;
+
+ const std::string &ReplacementText = BuildReplaceText(
+ ModeArg, *Result.SourceManager, Result.Context->getLangOpts());
+
+ diag(ModeArg->getLocStart(), "use %0 mode 'e' to set O_CLOEXEC")
+ << FD
+ << FixItHint::CreateReplacement(ModeArg->getSourceRange(),
+ ReplacementText);
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- CloexecFopenCheck.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_ANDROID_CLOEXEC_FOPEN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// fopen() is suggested to include "e" in their mode string; like "re" would be
+/// better than "r".
+///
+/// This check only works when corresponding argument is StringLiteral. No
+/// constant propagation.
+///
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html
+class CloexecFopenCheck : public ClangTidyCheck {
+public:
+ CloexecFopenCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
--- /dev/null
+//===--- CloexecOpenCheck.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 "CloexecOpenCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+static constexpr const char *O_CLOEXEC = "O_CLOEXEC";
+
+void CloexecOpenCheck::registerMatchers(MatchFinder *Finder) {
+ auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+ hasAnyName("open", "open64"),
+ hasParameter(0, CharPointerType),
+ hasParameter(1, hasType(isInteger())))
+ .bind("funcDecl")))
+ .bind("openFn"),
+ this);
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+ hasName("openat"),
+ hasParameter(0, hasType(isInteger())),
+ hasParameter(1, CharPointerType),
+ hasParameter(2, hasType(isInteger())))
+ .bind("funcDecl")))
+ .bind("openatFn"),
+ this);
+}
+
+void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
+ const Expr *FlagArg = nullptr;
+ if (const auto *OpenFnCall = Result.Nodes.getNodeAs<CallExpr>("openFn"))
+ FlagArg = OpenFnCall->getArg(1);
+ else if (const auto *OpenFnCall =
+ Result.Nodes.getNodeAs<CallExpr>("openatFn"))
+ FlagArg = OpenFnCall->getArg(2);
+ assert(FlagArg);
+
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+
+ // Check the required flag.
+ SourceManager &SM = *Result.SourceManager;
+ if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
+ Result.Context->getLangOpts(), O_CLOEXEC))
+ return;
+
+ SourceLocation EndLoc =
+ Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
+ Result.Context->getLangOpts());
+
+ diag(EndLoc, "%0 should use %1 where possible")
+ << FD << O_CLOEXEC
+ << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + O_CLOEXEC).str());
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- CloexecOpenCheck.h - clang-tidy-----------------------------------===//
+//
+// 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_ANDROID_CLOEXEC_OPEN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// Finds code that opens file without using the O_CLOEXEC flag.
+///
+/// open(), openat(), and open64() had better to include O_CLOEXEC in their
+/// flags argument. Only consider simple cases that the corresponding argument
+/// is constant or binary operation OR among constants like 'O_CLOEXEC' or
+/// 'O_CLOEXEC | O_RDONLY'. No constant propagation is performed.
+///
+/// Only the symbolic 'O_CLOEXEC' macro definition is checked, not the concrete
+/// value.
+class CloexecOpenCheck : public ClangTidyCheck {
+public:
+ CloexecOpenCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
--- /dev/null
+//===--- CloexecSocketCheck.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 "CloexecSocketCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+static constexpr const char *SOCK_CLOEXEC = "SOCK_CLOEXEC";
+
+void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+ hasName("socket"),
+ hasParameter(0, hasType(isInteger())),
+ hasParameter(1, hasType(isInteger())),
+ hasParameter(2, hasType(isInteger())))
+ .bind("funcDecl")))
+ .bind("socketFn"),
+ this);
+}
+
+void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("socketFn");
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+ const Expr *FlagArg = MatchedCall->getArg(1);
+ SourceManager &SM = *Result.SourceManager;
+
+ if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
+ Result.Context->getLangOpts(), SOCK_CLOEXEC))
+ return;
+
+ SourceLocation EndLoc =
+ Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
+ Result.Context->getLangOpts());
+
+ diag(EndLoc, "%0 should use %1 where possible")
+ << FD << SOCK_CLOEXEC
+ << FixItHint::CreateInsertion(EndLoc,
+ (Twine(" | ") + SOCK_CLOEXEC).str());
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- CloexecSocketCheck.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_ANDROID_CLOEXEC_SOCKET_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// Finds code that uses socket() without using the SOCK_CLOEXEC flag.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html
+class CloexecSocketCheck : public ClangTidyCheck {
+public:
+ CloexecSocketCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
--- /dev/null
+//===------- BoostTidyModule.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 "UseToStringCheck.h"
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+class BoostModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
+ }
+};
+
+// Register the BoostModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<BoostModule> X("boost-module",
+ "Add boost checks.");
+
+} // namespace boost
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the BoostModule.
+volatile int BoostModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyBoostModule
+ BoostTidyModule.cpp
+ UseToStringCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ )
--- /dev/null
+//===--- UseToStringCheck.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 "UseToStringCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+AST_MATCHER(Type, isStrictlyInteger) {
+ return Node.isIntegerType() && !Node.isAnyCharacterType() &&
+ !Node.isBooleanType();
+}
+
+void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ callExpr(
+ hasDeclaration(functionDecl(
+ returns(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("std::basic_string"),
+ hasTemplateArgument(0,
+ templateArgument().bind("char_type"))))),
+ hasName("boost::lexical_cast"),
+ hasParameter(0, hasType(qualType(has(substTemplateTypeParmType(
+ isStrictlyInteger()))))))),
+ argumentCountIs(1), unless(isInTemplateInstantiation()))
+ .bind("to_string"),
+ this);
+}
+
+void UseToStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("to_string");
+ auto CharType =
+ Result.Nodes.getNodeAs<TemplateArgument>("char_type")->getAsType();
+
+ StringRef StringType;
+ if (CharType->isSpecificBuiltinType(BuiltinType::Char_S) ||
+ CharType->isSpecificBuiltinType(BuiltinType::Char_U))
+ StringType = "string";
+ else if (CharType->isSpecificBuiltinType(BuiltinType::WChar_S) ||
+ CharType->isSpecificBuiltinType(BuiltinType::WChar_U))
+ StringType = "wstring";
+ else
+ return;
+
+ auto Loc = Call->getLocStart();
+ auto Diag =
+ diag(Loc, "use std::to_%0 instead of boost::lexical_cast<std::%0>")
+ << StringType;
+
+ if (Loc.isMacroID())
+ return;
+
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(Call->getLocStart(),
+ Call->getArg(0)->getLocStart()),
+ (llvm::Twine("std::to_") + StringType + "(").str());
+}
+
+} // namespace boost
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseToStringCheck.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_BOOST_USE_TO_STRING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+/// Finds calls to ``boost::lexical_cast<std::string>`` and
+/// ``boost::lexical_cast<std::wstring>`` and replaces them with
+/// ``std::to_string`` and ``std::to_wstring`` calls.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/boost-use-to-string.html
+class UseToStringCheck : public ClangTidyCheck {
+public:
+ UseToStringCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace boost
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
--- /dev/null
+//===--- BugproneTidyModule.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 "SuspiciousMemsetUsageCheck.h"
+#include "UndefinedMemoryManipulationCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+class BugproneModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
+ "bugprone-suspicious-memset-usage");
+ CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
+ "bugprone-undefined-memory-manipulation");
+ }
+};
+
+} // namespace bugprone
+
+// Register the BugproneTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<bugprone::BugproneModule>
+ X("bugprone-module", "Adds checks for bugprone code constructs.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the BugproneModule.
+volatile int BugproneModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyBugproneModule
+ BugproneTidyModule.cpp
+ SuspiciousMemsetUsageCheck.cpp
+ UndefinedMemoryManipulationCheck.cpp
+
+ LINK_LIBS
+ clangAnalysis
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ clangTooling
+ )
--- /dev/null
+//===--- SuspiciousMemsetUsageCheck.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 "SuspiciousMemsetUsageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void SuspiciousMemsetUsageCheck::registerMatchers(MatchFinder *Finder) {
+ // Note: void *memset(void *buffer, int fill_char, size_t byte_count);
+ // Look for memset(x, '0', z). Probably memset(x, 0, z) was intended.
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(hasName("::memset"))),
+ hasArgument(1, characterLiteral(equals(static_cast<unsigned>('0')))
+ .bind("char-zero-fill")),
+ unless(
+ eachOf(hasArgument(0, anyOf(hasType(pointsTo(isAnyCharacter())),
+ hasType(arrayType(hasElementType(
+ isAnyCharacter()))))),
+ isInTemplateInstantiation()))),
+ this);
+
+ // Look for memset with an integer literal in its fill_char argument.
+ // Will check if it gets truncated.
+ Finder->addMatcher(callExpr(callee(functionDecl(hasName("::memset"))),
+ hasArgument(1, integerLiteral().bind("num-fill")),
+ unless(isInTemplateInstantiation())),
+ this);
+
+ // Look for memset(x, y, 0) as that is most likely an argument swap.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::memset"))),
+ unless(hasArgument(1, anyOf(characterLiteral(equals(
+ static_cast<unsigned>('0'))),
+ integerLiteral()))),
+ unless(isInTemplateInstantiation()))
+ .bind("call"),
+ this);
+}
+
+void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *CharZeroFill =
+ Result.Nodes.getNodeAs<CharacterLiteral>("char-zero-fill")) {
+ // Case 1: fill_char of memset() is a character '0'. Probably an
+ // integer zero was intended.
+
+ SourceRange CharRange = CharZeroFill->getSourceRange();
+ auto Diag =
+ diag(CharZeroFill->getLocStart(), "memset fill value is char '0', "
+ "potentially mistaken for int 0");
+
+ // Only suggest a fix if no macros are involved.
+ if (CharRange.getBegin().isMacroID())
+ return;
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(CharRange), "0");
+ }
+
+ else if (const auto *NumFill =
+ Result.Nodes.getNodeAs<IntegerLiteral>("num-fill")) {
+ // Case 2: fill_char of memset() is larger in size than an unsigned char
+ // so it gets truncated during conversion.
+
+ llvm::APSInt NumValue;
+ const auto UCharMax = (1 << Result.Context->getCharWidth()) - 1;
+ if (!NumFill->EvaluateAsInt(NumValue, *Result.Context) ||
+ (NumValue >= 0 && NumValue <= UCharMax))
+ return;
+
+ diag(NumFill->getLocStart(), "memset fill value is out of unsigned "
+ "character range, gets truncated");
+ }
+
+ else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
+ // Case 3: byte_count of memset() is zero. This is most likely an
+ // argument swap.
+
+ const Expr *FillChar = Call->getArg(1);
+ const Expr *ByteCount = Call->getArg(2);
+
+ // Return if `byte_count` is not zero at compile time.
+ llvm::APSInt Value1, Value2;
+ if (ByteCount->isValueDependent() ||
+ !ByteCount->EvaluateAsInt(Value2, *Result.Context) || Value2 != 0)
+ return;
+
+ // Return if `fill_char` is known to be zero or negative at compile
+ // time. In these cases, swapping the args would be a nop, or
+ // introduce a definite bug. The code is likely correct.
+ if (!FillChar->isValueDependent() &&
+ FillChar->EvaluateAsInt(Value1, *Result.Context) &&
+ (Value1 == 0 || Value1.isNegative()))
+ return;
+
+ // `byte_count` is known to be zero at compile time, and `fill_char` is
+ // either not known or known to be a positive integer. Emit a warning
+ // and fix-its to swap the arguments.
+ auto D = diag(Call->getLocStart(),
+ "memset of size zero, potentially swapped arguments");
+ StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context);
+ StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context);
+ if (LHSString.empty() || RHSString.empty())
+ return;
+
+ D << tooling::fixit::createReplacement(*FillChar, RHSString)
+ << tooling::fixit::createReplacement(*ByteCount, LHSString);
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SuspiciousMemsetUsageCheck.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_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds memset calls with potential mistakes in their arguments.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memset-usage.html
+class SuspiciousMemsetUsageCheck : public ClangTidyCheck {
+public:
+ SuspiciousMemsetUsageCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
--- /dev/null
+//===--- UndefinedMemoryManipulationCheck.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 "UndefinedMemoryManipulationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) {
+ return !Node.isTriviallyCopyable();
+}
+} // namespace
+
+void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) {
+ const auto NotTriviallyCopyableObject =
+ hasType(pointsTo(cxxRecordDecl(isNotTriviallyCopyable())));
+
+ // Check whether destination object is not TriviallyCopyable.
+ // Applicable to all three memory manipulation functions.
+ Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName(
+ "::memset", "::memcpy", "::memmove"))),
+ hasArgument(0, NotTriviallyCopyableObject))
+ .bind("dest"),
+ this);
+
+ // Check whether source object is not TriviallyCopyable.
+ // Only applicable to memcpy() and memmove().
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasAnyName("::memcpy", "::memmove"))),
+ hasArgument(1, NotTriviallyCopyableObject))
+ .bind("src"),
+ this);
+}
+
+void UndefinedMemoryManipulationCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *Destination = Result.Nodes.getNodeAs<CallExpr>("dest")) {
+ diag(Destination->getLocStart(), "undefined behavior, destination "
+ "object is not TriviallyCopyable");
+ }
+ if (const auto *Source = Result.Nodes.getNodeAs<CallExpr>("src")) {
+ diag(Source->getLocStart(), "undefined behavior, source object is not "
+ "TriviallyCopyable");
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UndefinedMemoryManipulationCheck.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_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+/// ``memmove()`` on not TriviallyCopyable objects resulting in undefined
+/// behavior.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-undefined-memory-manipulation.html
+class UndefinedMemoryManipulationCheck : public ClangTidyCheck {
+public:
+ UndefinedMemoryManipulationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
--- /dev/null
+//===--- CERTTidyModule.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 "../google/UnnamedNamespaceInHeaderCheck.h"
+#include "../misc/MoveConstructorInitCheck.h"
+#include "../misc/NewDeleteOverloadsCheck.h"
+#include "../misc/NonCopyableObjects.h"
+#include "../misc/StaticAssertCheck.h"
+#include "../misc/ThrowByValueCatchByReferenceCheck.h"
+#include "CommandProcessorCheck.h"
+#include "DontModifyStdNamespaceCheck.h"
+#include "FloatLoopCounter.h"
+#include "LimitedRandomnessCheck.h"
+#include "PostfixOperatorCheck.h"
+#include "SetLongJmpCheck.h"
+#include "StaticObjectExceptionCheck.h"
+#include "StrToNumCheck.h"
+#include "ThrownExceptionTypeCheck.h"
+#include "VariadicFunctionDefCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+class CERTModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ // C++ checkers
+ // DCL
+ CheckFactories.registerCheck<PostfixOperatorCheck>(
+ "cert-dcl21-cpp");
+ CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
+ CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
+ "cert-dcl54-cpp");
+ CheckFactories.registerCheck<DontModifyStdNamespaceCheck>(
+ "cert-dcl58-cpp");
+ CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
+ "cert-dcl59-cpp");
+ // OOP
+ CheckFactories.registerCheck<misc::MoveConstructorInitCheck>(
+ "cert-oop11-cpp");
+ // ERR
+ CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
+ "cert-err09-cpp");
+ CheckFactories.registerCheck<SetLongJmpCheck>("cert-err52-cpp");
+ CheckFactories.registerCheck<StaticObjectExceptionCheck>("cert-err58-cpp");
+ CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
+ CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
+ "cert-err61-cpp");
+ // MSC
+ CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
+
+ // C checkers
+ // DCL
+ CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c");
+ // ENV
+ CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
+ // FLP
+ CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
+ // FIO
+ CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
+ // ERR
+ CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
+ // MSC
+ CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
+ }
+};
+
+} // namespace cert
+
+// Register the MiscTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<cert::CERTModule>
+ X("cert-module",
+ "Adds lint checks corresponding to CERT secure coding guidelines.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the CERTModule.
+volatile int CERTModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCERTModule
+ CERTTidyModule.cpp
+ CommandProcessorCheck.cpp
+ DontModifyStdNamespaceCheck.cpp
+ FloatLoopCounter.cpp
+ LimitedRandomnessCheck.cpp
+ PostfixOperatorCheck.cpp
+ SetLongJmpCheck.cpp
+ StaticObjectExceptionCheck.cpp
+ StrToNumCheck.cpp
+ ThrownExceptionTypeCheck.cpp
+ VariadicFunctionDefCheck.cpp
+
+ LINK_LIBS
+ clangAnalysis
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyGoogleModule
+ clangTidyMiscModule
+ clangTidyUtils
+ )
--- /dev/null
+//===--- Env33CCheck.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 "CommandProcessorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void CommandProcessorCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(anyOf(hasName("::system"), hasName("::popen"),
+ hasName("::_popen")))
+ .bind("func")),
+ // Do not diagnose when the call expression passes a null pointer
+ // constant to system(); that only checks for the presence of a
+ // command processor, which is not a security risk by itself.
+ unless(callExpr(callee(functionDecl(hasName("::system"))),
+ argumentCountIs(1),
+ hasArgument(0, nullPointerConstant()))))
+ .bind("expr"),
+ this);
+}
+
+void CommandProcessorCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Fn = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *E = Result.Nodes.getNodeAs<CallExpr>("expr");
+
+ diag(E->getExprLoc(), "calling %0 uses a command processor") << Fn;
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- CommandInterpreterCheck.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_CERT_COMMAND_PROCESSOR_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_COMMAND_PROCESSOR_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Execution of a command processor can lead to security vulnerabilities,
+/// and is generally not required. Instead, prefer to launch executables
+/// directly via mechanisms that give you more control over what executable is
+/// actually launched.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-env33-c.html
+class CommandProcessorCheck : public ClangTidyCheck {
+public:
+ CommandProcessorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_COMMAND_PROCESSOR_CHECK_H
--- /dev/null
+//===--- DontModifyStdNamespaceCheck.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 "DontModifyStdNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ namespaceDecl(unless(isExpansionInSystemHeader()),
+ anyOf(hasName("std"), hasName("posix")),
+ has(decl(unless(anyOf(
+ functionDecl(isExplicitTemplateSpecialization()),
+ cxxRecordDecl(isExplicitTemplateSpecialization()))))))
+ .bind("nmspc"),
+ this);
+}
+
+void DontModifyStdNamespaceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
+
+ // Only consider top level namespaces.
+ if (N->getParent() != Result.Context->getTranslationUnitDecl())
+ return;
+
+ diag(N->getLocation(),
+ "modification of %0 namespace can result in undefined behavior")
+ << N;
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DontModifyStdNamespaceCheck.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_CERT_DONT_MODIFY_STD_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Modification of the std or posix namespace can result in undefined behavior.
+/// This check warns for such modifications.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc53-cpp.html
+class DontModifyStdNamespaceCheck : public ClangTidyCheck {
+public:
+ DontModifyStdNamespaceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
--- /dev/null
+//===--- FloatLoopCounter.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 "FloatLoopCounter.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void FloatLoopCounter::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ forStmt(hasIncrement(expr(hasType(realFloatingPointType())))).bind("for"),
+ this);
+}
+
+void FloatLoopCounter::check(const MatchFinder::MatchResult &Result) {
+ const auto *FS = Result.Nodes.getNodeAs<ForStmt>("for");
+
+ diag(FS->getInc()->getExprLoc(), "loop induction expression should not have "
+ "floating-point type");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- FloatLoopCounter.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_CERT_FLOAT_LOOP_COUNTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_FLOAT_LOOP_COUNTER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// This check diagnoses when the loop induction expression of a for loop has
+/// floating-point type. The check corresponds to:
+/// https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-flp30-c.html
+class FloatLoopCounter : public ClangTidyCheck {
+public:
+ FloatLoopCounter(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_FLOAT_LOOP_COUNTER_H
--- /dev/null
+------------------------------------------------------------------------------
+clang-tidy CERT Files
+------------------------------------------------------------------------------
+All clang-tidy files are licensed under the LLVM license with the following
+additions:
+
+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.
+
+The foregoing is permitted by the Terms of Use as follows:
+"Linking to the Service
+Because we update many of our Web documents regularly, we would prefer that you
+link to our Web pages whenever possible rather than reproduce them. It is not
+necessary to request permission to make referential hypertext links to The
+Service."
+http://www.sei.cmu.edu/legal/ip/index.cfm.
+
+Please allow this letter to also confirm that no formal permission is required
+to reproduce the title of the content being linked to, nor to reproduce any
+de Minimis description of such content.
--- /dev/null
+//===--- LimitedRandomnessCheck.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 "LimitedRandomnessCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void LimitedRandomnessCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(callExpr(callee(functionDecl(namedDecl(hasName("::rand")),
+ parameterCountIs(0))))
+ .bind("randomGenerator"),
+ this);
+}
+
+void LimitedRandomnessCheck::check(const MatchFinder::MatchResult &Result) {
+ std::string msg = "";
+ if (getLangOpts().CPlusPlus)
+ msg = "; use C++11 random library instead";
+
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("randomGenerator");
+ diag(MatchedDecl->getLocStart(), "rand() has limited randomness" + msg);
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- LimitedRandomnessCheck.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_CERT_LIMITED_RANDOMNESS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Pseudorandom number generators are not genuinely random. The result of the
+/// std::rand() function makes no guarantees as to the quality of the random
+/// sequence produced.
+/// This check warns for the usage of std::rand() function.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc50-cpp.html
+class LimitedRandomnessCheck : public ClangTidyCheck {
+public:
+ LimitedRandomnessCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
--- /dev/null
+//===--- PostfixOperatorCheck.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 "PostfixOperatorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void PostfixOperatorCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("++"),
+ hasOverloadedOperatorName("--")))
+ .bind("decl"),
+ this);
+}
+
+void PostfixOperatorCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+
+ bool HasThis = false;
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
+ HasThis = MethodDecl->isInstance();
+
+ // Check if the operator is a postfix one.
+ if (FuncDecl->getNumParams() != (HasThis ? 1 : 2))
+ return;
+
+ SourceRange ReturnRange = FuncDecl->getReturnTypeSourceRange();
+ SourceLocation Location = ReturnRange.getBegin();
+ if (!Location.isValid())
+ return;
+
+ QualType ReturnType = FuncDecl->getReturnType();
+
+ // Warn when the operators return a reference.
+ if (const auto *RefType = ReturnType->getAs<ReferenceType>()) {
+ auto Diag = diag(Location, "overloaded %0 returns a reference instead of a "
+ "constant object type")
+ << FuncDecl;
+
+ if (Location.isMacroID() || ReturnType->getAs<TypedefType>() ||
+ RefType->getPointeeTypeAsWritten()->getAs<TypedefType>())
+ return;
+
+ QualType ReplaceType =
+ ReturnType.getNonReferenceType().getLocalUnqualifiedType();
+ // The getReturnTypeSourceRange omits the qualifiers. We do not want to
+ // duplicate the const.
+ if (!ReturnType->getPointeeType().isConstQualified())
+ ReplaceType.addConst();
+
+ Diag << FixItHint::CreateReplacement(
+ ReturnRange,
+ ReplaceType.getAsString(Result.Context->getPrintingPolicy()) + " ");
+
+ return;
+ }
+
+ if (ReturnType.isConstQualified() || ReturnType->isBuiltinType() ||
+ ReturnType->isPointerType())
+ return;
+
+ auto Diag =
+ diag(Location, "overloaded %0 returns a non-constant object instead of a "
+ "constant object type")
+ << FuncDecl;
+
+ if (!Location.isMacroID() && !ReturnType->getAs<TypedefType>())
+ Diag << FixItHint::CreateInsertion(Location, "const ");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- PostfixOperatorCheck.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_CERT_POSTFIX_OPERATOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks if the overloaded postfix ++ and -- operator return a constant
+/// object.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-postfix-operator.html
+class PostfixOperatorCheck : public ClangTidyCheck {
+public:
+ PostfixOperatorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
--- /dev/null
+//===--- SetLongJmpCheck.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 "SetLongJmpCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+const char SetLongJmpCheck::DiagWording[] =
+ "do not call %0; consider using exception handling instead";
+
+namespace {
+class SetJmpMacroCallbacks : public PPCallbacks {
+ SetLongJmpCheck &Check;
+
+public:
+ explicit SetJmpMacroCallbacks(SetLongJmpCheck &Check) : Check(Check) {}
+
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override {
+ const auto *II = MacroNameTok.getIdentifierInfo();
+ if (!II)
+ return;
+
+ if (II->getName() == "setjmp")
+ Check.diag(Range.getBegin(), Check.DiagWording) << II;
+ }
+};
+} // namespace
+
+void SetLongJmpCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ // This checker only applies to C++, where exception handling is a superior
+ // solution to setjmp/longjmp calls.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Per [headers]p5, setjmp must be exposed as a macro instead of a function,
+ // despite the allowance in C for setjmp to also be an extern function.
+ Compiler.getPreprocessor().addPPCallbacks(
+ llvm::make_unique<SetJmpMacroCallbacks>(*this));
+}
+
+void SetLongJmpCheck::registerMatchers(MatchFinder *Finder) {
+ // This checker only applies to C++, where exception handling is a superior
+ // solution to setjmp/longjmp calls.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // In case there is an implementation that happens to define setjmp as a
+ // function instead of a macro, this will also catch use of it. However, we
+ // are primarily searching for uses of longjmp.
+ Finder->addMatcher(callExpr(callee(functionDecl(anyOf(hasName("setjmp"),
+ hasName("longjmp")))))
+ .bind("expr"),
+ this);
+}
+
+void SetLongJmpCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<CallExpr>("expr");
+ diag(E->getExprLoc(), DiagWording) << cast<NamedDecl>(E->getCalleeDecl());
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SetLongJmpCheck.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_SETLONGJMPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against use of setjmp/longjmp in C++ code
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err52-cpp.html
+class SetLongJmpCheck : public ClangTidyCheck {
+public:
+ SetLongJmpCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+ static const char DiagWording[];
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
--- /dev/null
+//===--- StaticObjectExceptionCheck.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 "StaticObjectExceptionCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
+ if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions))
+ return;
+
+ // Match any static or thread_local variable declaration that has an
+ // initializer that can throw.
+ Finder->addMatcher(
+ varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
+ unless(hasAncestor(functionDecl())),
+ anyOf(hasDescendant(cxxConstructExpr(hasDeclaration(
+ cxxConstructorDecl(unless(isNoThrow())).bind("func")))),
+ hasDescendant(cxxNewExpr(hasDeclaration(
+ functionDecl(unless(isNoThrow())).bind("func")))),
+ hasDescendant(callExpr(hasDeclaration(
+ functionDecl(unless(isNoThrow())).bind("func"))))))
+ .bind("var"),
+ this);
+}
+
+void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ diag(VD->getLocation(),
+ "initialization of %0 with %select{static|thread_local}1 storage "
+ "duration may throw an exception that cannot be caught")
+ << VD << (VD->getStorageDuration() == SD_Static ? 0 : 1);
+
+ SourceLocation FuncLocation = Func->getLocation();
+ if (FuncLocation.isValid()) {
+ diag(FuncLocation,
+ "possibly throwing %select{constructor|function}0 declared here",
+ DiagnosticIDs::Note)
+ << (isa<CXXConstructorDecl>(Func) ? 0 : 1);
+ }
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StaticObjectExceptionCheck.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_CERT_ERR58_CPP_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks whether the constructor for a static or thread_local object will
+/// throw.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err58-cpp.html
+class StaticObjectExceptionCheck : public ClangTidyCheck {
+public:
+ StaticObjectExceptionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H
--- /dev/null
+//===--- Err34CCheck.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 "StrToNumCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/FormatString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <cassert>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void StrToNumCheck::registerMatchers(MatchFinder *Finder) {
+ // Match any function call to the C standard library string conversion
+ // functions that do no error checking.
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(anyOf(
+ functionDecl(hasAnyName("::atoi", "::atof", "::atol", "::atoll"))
+ .bind("converter"),
+ functionDecl(hasAnyName("::scanf", "::sscanf", "::fscanf",
+ "::vfscanf", "::vscanf", "::vsscanf"))
+ .bind("formatted")))))
+ .bind("expr"),
+ this);
+}
+
+namespace {
+enum class ConversionKind {
+ None,
+ ToInt,
+ ToUInt,
+ ToLongInt,
+ ToLongUInt,
+ ToIntMax,
+ ToUIntMax,
+ ToFloat,
+ ToDouble,
+ ToLongDouble
+};
+
+ConversionKind ClassifyConversionFunc(const FunctionDecl *FD) {
+ return llvm::StringSwitch<ConversionKind>(FD->getName())
+ .Cases("atoi", "atol", ConversionKind::ToInt)
+ .Case("atoll", ConversionKind::ToLongInt)
+ .Case("atof", ConversionKind::ToDouble)
+ .Default(ConversionKind::None);
+}
+
+ConversionKind ClassifyFormatString(StringRef Fmt, const LangOptions &LO,
+ const TargetInfo &TI) {
+ // Scan the format string for the first problematic format specifier, then
+ // report that as the conversion type. This will miss additional conversion
+ // specifiers, but that is acceptable behavior.
+
+ class Handler : public analyze_format_string::FormatStringHandler {
+ ConversionKind CK;
+
+ bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
+ const char *startSpecifier,
+ unsigned specifierLen) override {
+ // If we just consume the argument without assignment, we don't care
+ // about it having conversion errors.
+ if (!FS.consumesDataArgument())
+ return true;
+
+ // Get the conversion specifier and use it to determine the conversion
+ // kind.
+ analyze_scanf::ScanfConversionSpecifier SCS = FS.getConversionSpecifier();
+ if (SCS.isIntArg()) {
+ switch (FS.getLengthModifier().getKind()) {
+ case analyze_scanf::LengthModifier::AsLongLong:
+ CK = ConversionKind::ToLongInt;
+ break;
+ case analyze_scanf::LengthModifier::AsIntMax:
+ CK = ConversionKind::ToIntMax;
+ break;
+ default:
+ CK = ConversionKind::ToInt;
+ break;
+ }
+ } else if (SCS.isUIntArg()) {
+ switch (FS.getLengthModifier().getKind()) {
+ case analyze_scanf::LengthModifier::AsLongLong:
+ CK = ConversionKind::ToLongUInt;
+ break;
+ case analyze_scanf::LengthModifier::AsIntMax:
+ CK = ConversionKind::ToUIntMax;
+ break;
+ default:
+ CK = ConversionKind::ToUInt;
+ break;
+ }
+ } else if (SCS.isDoubleArg()) {
+ switch (FS.getLengthModifier().getKind()) {
+ case analyze_scanf::LengthModifier::AsLongDouble:
+ CK = ConversionKind::ToLongDouble;
+ break;
+ case analyze_scanf::LengthModifier::AsLong:
+ CK = ConversionKind::ToDouble;
+ break;
+ default:
+ CK = ConversionKind::ToFloat;
+ break;
+ }
+ }
+
+ // Continue if we have yet to find a conversion kind that we care about.
+ return CK == ConversionKind::None;
+ }
+
+ public:
+ Handler() : CK(ConversionKind::None) {}
+
+ ConversionKind get() const { return CK; }
+ };
+
+ Handler H;
+ analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
+
+ return H.get();
+}
+
+StringRef ClassifyConversionType(ConversionKind K) {
+ switch (K) {
+ case ConversionKind::None:
+ assert(false && "Unexpected conversion kind");
+ case ConversionKind::ToInt:
+ case ConversionKind::ToLongInt:
+ case ConversionKind::ToIntMax:
+ return "an integer value";
+ case ConversionKind::ToUInt:
+ case ConversionKind::ToLongUInt:
+ case ConversionKind::ToUIntMax:
+ return "an unsigned integer value";
+ case ConversionKind::ToFloat:
+ case ConversionKind::ToDouble:
+ case ConversionKind::ToLongDouble:
+ return "a floating-point value";
+ }
+ llvm_unreachable("Unknown conversion kind");
+}
+
+StringRef ClassifyReplacement(ConversionKind K) {
+ switch (K) {
+ case ConversionKind::None:
+ assert(false && "Unexpected conversion kind");
+ case ConversionKind::ToInt:
+ return "strtol";
+ case ConversionKind::ToUInt:
+ return "strtoul";
+ case ConversionKind::ToIntMax:
+ return "strtoimax";
+ case ConversionKind::ToLongInt:
+ return "strtoll";
+ case ConversionKind::ToLongUInt:
+ return "strtoull";
+ case ConversionKind::ToUIntMax:
+ return "strtoumax";
+ case ConversionKind::ToFloat:
+ return "strtof";
+ case ConversionKind::ToDouble:
+ return "strtod";
+ case ConversionKind::ToLongDouble:
+ return "strtold";
+ }
+ llvm_unreachable("Unknown conversion kind");
+}
+} // unnamed namespace
+
+void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr");
+ const FunctionDecl *FuncDecl = nullptr;
+ ConversionKind Conversion;
+
+ if (const auto *ConverterFunc =
+ Result.Nodes.getNodeAs<FunctionDecl>("converter")) {
+ // Converter functions are always incorrect to use.
+ FuncDecl = ConverterFunc;
+ Conversion = ClassifyConversionFunc(ConverterFunc);
+ } else if (const auto *FFD =
+ Result.Nodes.getNodeAs<FunctionDecl>("formatted")) {
+ StringRef FmtStr;
+ // The format string comes from the call expression and depends on which
+ // flavor of scanf is called.
+ // Index 0: scanf, vscanf, Index 1: fscanf, sscanf, vfscanf, vsscanf.
+ unsigned Idx =
+ (FFD->getName() == "scanf" || FFD->getName() == "vscanf") ? 0 : 1;
+
+ // Given the index, see if the call expression argument at that index is
+ // a string literal.
+ if (Call->getNumArgs() < Idx)
+ return;
+
+ if (const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {
+ if (const auto *SL = dyn_cast<StringLiteral>(Arg)) {
+ FmtStr = SL->getString();
+ }
+ }
+
+ // If we could not get the format string, bail out.
+ if (FmtStr.empty())
+ return;
+
+ // Formatted input functions need further checking of the format string to
+ // determine whether a problematic conversion may be happening.
+ Conversion = ClassifyFormatString(FmtStr, getLangOpts(),
+ Result.Context->getTargetInfo());
+ if (Conversion != ConversionKind::None)
+ FuncDecl = FFD;
+ }
+
+ if (!FuncDecl)
+ return;
+
+ diag(Call->getExprLoc(),
+ "%0 used to convert a string to %1, but function will not report "
+ "conversion errors; consider using '%2' instead")
+ << FuncDecl << ClassifyConversionType(Conversion)
+ << ClassifyReplacement(Conversion);
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StrToNumCheck.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_CERT_STRTONUMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_STRTONUMCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against use of string conversion functions that do not have
+/// reasonable error handling for conversion errors.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err34-c.html
+class StrToNumCheck : public ClangTidyCheck {
+public:
+ StrToNumCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_STRTONUMCHECK_H
--- /dev/null
+//===--- ThrownExceptionTypeCheck.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 "ThrownExceptionTypeCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cxxThrowExpr(has(ignoringParenImpCasts(
+ cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
+ isCopyConstructor(), unless(isNoThrow()))))
+ .bind("expr")))),
+ this);
+}
+
+void ThrownExceptionTypeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+ diag(E->getExprLoc(),
+ "thrown exception type is not nothrow copy constructible");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ThrownExceptionTypeCheck.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_CERT_THROWNEXCEPTIONTYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks whether a thrown object is nothrow copy constructible.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err60-cpp.html
+class ThrownExceptionTypeCheck : public ClangTidyCheck {
+public:
+ ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
--- /dev/null
+//===--- VariadicfunctiondefCheck.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 "VariadicFunctionDefCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void VariadicFunctionDefCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // We only care about function *definitions* that are variadic, and do not
+ // have extern "C" language linkage.
+ Finder->addMatcher(
+ functionDecl(isDefinition(), isVariadic(), unless(isExternC()))
+ .bind("func"),
+ this);
+}
+
+void VariadicFunctionDefCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ diag(FD->getLocation(),
+ "do not define a C-style variadic function; consider using a function "
+ "parameter pack or currying instead");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- VariadicFunctionDefCheck.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_CERT_VARIADICFUNCTIONDEF_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_VARIADICFUNCTIONDEF_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against any C-style variadic function definitions (not declarations).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl50-cpp.html
+class VariadicFunctionDefCheck : public ClangTidyCheck {
+public:
+ VariadicFunctionDefCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_VARIADICFUNCTIONDEF_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCppCoreGuidelinesModule
+ CppCoreGuidelinesTidyModule.cpp
+ InterfacesGlobalInitCheck.cpp
+ NoMallocCheck.cpp
+ ProBoundsArrayToPointerDecayCheck.cpp
+ ProBoundsConstantArrayIndexCheck.cpp
+ ProBoundsPointerArithmeticCheck.cpp
+ ProTypeConstCastCheck.cpp
+ ProTypeCstyleCastCheck.cpp
+ ProTypeMemberInitCheck.cpp
+ ProTypeReinterpretCastCheck.cpp
+ ProTypeStaticCastDowncastCheck.cpp
+ ProTypeUnionAccessCheck.cpp
+ ProTypeVarargCheck.cpp
+ SpecialMemberFunctionsCheck.cpp
+ SlicingCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyMiscModule
+ clangTidyUtils
+ clangTooling
+ )
--- /dev/null
+//===--- CppCoreGuidelinesModule.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 "../misc/UnconventionalAssignOperatorCheck.h"
+#include "InterfacesGlobalInitCheck.h"
+#include "NoMallocCheck.h"
+#include "ProBoundsArrayToPointerDecayCheck.h"
+#include "ProBoundsConstantArrayIndexCheck.h"
+#include "ProBoundsPointerArithmeticCheck.h"
+#include "ProTypeConstCastCheck.h"
+#include "ProTypeCstyleCastCheck.h"
+#include "ProTypeMemberInitCheck.h"
+#include "ProTypeReinterpretCastCheck.h"
+#include "ProTypeStaticCastDowncastCheck.h"
+#include "ProTypeUnionAccessCheck.h"
+#include "ProTypeVarargCheck.h"
+#include "SlicingCheck.h"
+#include "SpecialMemberFunctionsCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// A module containing checks of the C++ Core Guidelines
+class CppCoreGuidelinesModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
+ "cppcoreguidelines-interfaces-global-init");
+ CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
+ CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
+ "cppcoreguidelines-pro-bounds-array-to-pointer-decay");
+ CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
+ "cppcoreguidelines-pro-bounds-constant-array-index");
+ CheckFactories.registerCheck<ProBoundsPointerArithmeticCheck>(
+ "cppcoreguidelines-pro-bounds-pointer-arithmetic");
+ CheckFactories.registerCheck<ProTypeConstCastCheck>(
+ "cppcoreguidelines-pro-type-const-cast");
+ CheckFactories.registerCheck<ProTypeCstyleCastCheck>(
+ "cppcoreguidelines-pro-type-cstyle-cast");
+ CheckFactories.registerCheck<ProTypeMemberInitCheck>(
+ "cppcoreguidelines-pro-type-member-init");
+ CheckFactories.registerCheck<ProTypeReinterpretCastCheck>(
+ "cppcoreguidelines-pro-type-reinterpret-cast");
+ CheckFactories.registerCheck<ProTypeStaticCastDowncastCheck>(
+ "cppcoreguidelines-pro-type-static-cast-downcast");
+ CheckFactories.registerCheck<ProTypeUnionAccessCheck>(
+ "cppcoreguidelines-pro-type-union-access");
+ CheckFactories.registerCheck<ProTypeVarargCheck>(
+ "cppcoreguidelines-pro-type-vararg");
+ CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
+ "cppcoreguidelines-special-member-functions");
+ CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
+ CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
+ "cppcoreguidelines-c-copy-assignment-signature");
+ }
+};
+
+// Register the LLVMTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<CppCoreGuidelinesModule>
+ X("cppcoreguidelines-module", "Adds checks for the C++ Core Guidelines.");
+
+} // namespace cppcoreguidelines
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the CppCoreGuidelinesModule.
+volatile int CppCoreGuidelinesModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- InterfacesGlobalInitCheck.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 "InterfacesGlobalInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) {
+ const auto IsGlobal =
+ allOf(hasGlobalStorage(),
+ hasDeclContext(anyOf(translationUnitDecl(), // Global scope.
+ namespaceDecl(), // Namespace scope.
+ recordDecl())), // Class scope.
+ unless(isConstexpr()));
+
+ const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration(
+ varDecl(IsGlobal, unless(isDefinition())).bind("referencee")));
+
+ Finder->addMatcher(
+ varDecl(IsGlobal, isDefinition(),
+ hasInitializer(expr(hasDescendant(ReferencesUndefinedGlobalVar))))
+ .bind("var"),
+ this);
+}
+
+void InterfacesGlobalInitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *const Var = Result.Nodes.getNodeAs<VarDecl>("var");
+ // For now assume that people who write macros know what they're doing.
+ if (Var->getLocation().isMacroID())
+ return;
+ const auto *const Referencee = Result.Nodes.getNodeAs<VarDecl>("referencee");
+ // If the variable has been defined, we're good.
+ const auto *const ReferenceeDef = Referencee->getDefinition();
+ if (ReferenceeDef != nullptr &&
+ Result.SourceManager->isBeforeInTranslationUnit(
+ ReferenceeDef->getLocation(), Var->getLocation())) {
+ return;
+ }
+ diag(Var->getLocation(),
+ "initializing non-local variable with non-const expression depending on "
+ "uninitialized non-local variable %0")
+ << Referencee;
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- InterfacesGlobalInitCheck.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_INTERFACES_GLOBAL_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags possible initialization order issues of static variables.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.html
+class InterfacesGlobalInitCheck : public ClangTidyCheck {
+public:
+ InterfacesGlobalInitCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
--- /dev/null
+//===--- NoMallocCheck.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 "NoMallocCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
+ const std::vector<std::string> NameList =
+ utils::options::parseStringList(FunctionNames);
+ return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
+}
+}
+
+void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "Allocations", AllocList);
+ Options.store(Opts, "Reallocations", ReallocList);
+ Options.store(Opts, "Deallocations", DeallocList);
+}
+
+void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
+ // C-style memory management is only problematic in C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Registering malloc, will suggest RAII.
+ Finder->addMatcher(callExpr(callee(functionDecl(hasAnyListedName(AllocList))))
+ .bind("allocation"),
+ this);
+
+ // Registering realloc calls, suggest std::vector or std::string.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasAnyListedName(ReallocList))))
+ .bind("realloc"),
+ this);
+
+ // Registering free calls, will suggest RAII instead.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasAnyListedName(DeallocList))))
+ .bind("free"),
+ this);
+}
+
+void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
+ const CallExpr *Call = nullptr;
+ StringRef Recommendation;
+
+ if ((Call = Result.Nodes.getNodeAs<CallExpr>("allocation")))
+ Recommendation = "consider a container or a smart pointer";
+ else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
+ Recommendation = "consider std::vector or std::string";
+ else if ((Call = Result.Nodes.getNodeAs<CallExpr>("free")))
+ Recommendation = "use RAII";
+
+ assert(Call && "Unhandled binding in the Matcher");
+
+ diag(Call->getLocStart(), "do not manage memory manually; %0")
+ << Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd());
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NoMallocCheck.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_NO_MALLOC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
+
+#include "../ClangTidy.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This checker is concerned with C-style memory management and suggest modern
+/// alternatives to it.
+/// The check is only enabled in C++. For analyzing malloc calls see Clang
+/// Static Analyzer - unix.Malloc.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
+class NoMallocCheck : public ClangTidyCheck {
+public:
+ /// Construct Checker and read in configuration for function names.
+ NoMallocCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllocList(Options.get("Allocations", "::malloc;::calloc")),
+ ReallocList(Options.get("Reallocations", "::realloc")),
+ DeallocList(Options.get("Deallocations", "::free")) {}
+
+ /// Make configuration of checker discoverable.
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+ /// Registering for malloc, calloc, realloc and free calls.
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+
+ /// Checks matched function calls and gives suggestion to modernize the code.
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ /// Semicolon-separated list of fully qualified names of memory allocation
+ /// functions the check warns about. Defaults to `::malloc;::calloc`.
+ const std::string AllocList;
+ /// Semicolon-separated list of fully qualified names of memory reallocation
+ /// functions the check warns about. Defaults to `::realloc`.
+ const std::string ReallocList;
+ /// Semicolon-separated list of fully qualified names of memory deallocation
+ /// functions the check warns about. Defaults to `::free`.
+ const std::string DeallocList;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
--- /dev/null
+//===--- ProBoundsArrayToPointerDecayCheck.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 "ProBoundsArrayToPointerDecayCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt,
+ ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
+ for (const DeclStmt *Stmt : {Node.getBeginStmt(), Node.getEndStmt()})
+ if (Stmt != nullptr && InnerMatcher.matches(*Stmt, Finder, Builder))
+ return true;
+ return false;
+}
+
+AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt) {
+ return stmt(hasAncestor(cxxForRangeStmt(
+ hasRangeBeginEndStmt(hasDescendant(equalsNode(&Node))))))
+ .matches(Node, Finder, Builder);
+}
+
+AST_MATCHER_P(Expr, hasParentIgnoringImpCasts,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ const Expr *E = &Node;
+ do {
+ ASTContext::DynTypedNodeList Parents =
+ Finder->getASTContext().getParents(*E);
+ if (Parents.size() != 1)
+ return false;
+ E = Parents[0].get<Expr>();
+ if (!E)
+ return false;
+ } while (isa<ImplicitCastExpr>(E));
+
+ return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+void ProBoundsArrayToPointerDecayCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // The only allowed array to pointer decay
+ // 1) just before array subscription
+ // 2) inside a range-for over an array
+ // 3) if it converts a string literal to a pointer
+ Finder->addMatcher(
+ implicitCastExpr(unless(hasParent(arraySubscriptExpr())),
+ unless(hasParentIgnoringImpCasts(explicitCastExpr())),
+ unless(isInsideOfRangeBeginEndStmt()),
+ unless(hasSourceExpression(stringLiteral())))
+ .bind("cast"),
+ this);
+}
+
+void ProBoundsArrayToPointerDecayCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
+ if (MatchedCast->getCastKind() != CK_ArrayToPointerDecay)
+ return;
+
+ diag(MatchedCast->getExprLoc(), "do not implicitly decay an array into a "
+ "pointer; consider using gsl::array_view or "
+ "an explicit cast instead");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProBoundsArrayToPointerDecayCheck.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_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all array to pointer decays
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.html
+class ProBoundsArrayToPointerDecayCheck : public ClangTidyCheck {
+public:
+ ProBoundsArrayToPointerDecayCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
--- /dev/null
+//===--- ProBoundsConstantArrayIndexCheck.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 "ProBoundsConstantArrayIndexCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void ProBoundsConstantArrayIndexCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "GslHeader", GslHeader);
+ Options.store(Opts, "IncludeStyle", IncludeStyle);
+}
+
+void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
+ CompilerInstance &Compiler) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Note: if a struct contains an array member, the compiler-generated
+ // constructor has an arraySubscriptExpr.
+ Finder->addMatcher(
+ arraySubscriptExpr(
+ hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
+ hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
+ .bind("expr"),
+ this);
+
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ hasOverloadedOperatorName("[]"),
+ hasArgument(
+ 0, hasType(cxxRecordDecl(hasName("::std::array")).bind("type"))),
+ hasArgument(1, expr().bind("index")))
+ .bind("expr"),
+ this);
+}
+
+void ProBoundsConstantArrayIndexCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Matched = Result.Nodes.getNodeAs<Expr>("expr");
+ const auto *IndexExpr = Result.Nodes.getNodeAs<Expr>("index");
+
+ if (IndexExpr->isValueDependent())
+ return; // We check in the specialization.
+
+ llvm::APSInt Index;
+ if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr,
+ /*isEvaluated=*/true)) {
+ SourceRange BaseRange;
+ if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched))
+ BaseRange = ArraySubscriptE->getBase()->getSourceRange();
+ else
+ BaseRange =
+ dyn_cast<CXXOperatorCallExpr>(Matched)->getArg(0)->getSourceRange();
+ SourceRange IndexRange = IndexExpr->getSourceRange();
+
+ auto Diag = diag(Matched->getExprLoc(),
+ "do not use array subscript when the index is "
+ "not an integer constant expression; use gsl::at() "
+ "instead");
+ if (!GslHeader.empty()) {
+ Diag << FixItHint::CreateInsertion(BaseRange.getBegin(), "gsl::at(")
+ << FixItHint::CreateReplacement(
+ SourceRange(BaseRange.getEnd().getLocWithOffset(1),
+ IndexRange.getBegin().getLocWithOffset(-1)),
+ ", ")
+ << FixItHint::CreateReplacement(Matched->getLocEnd(), ")");
+
+ Optional<FixItHint> Insertion = Inserter->CreateIncludeInsertion(
+ Result.SourceManager->getMainFileID(), GslHeader,
+ /*IsAngled=*/false);
+ if (Insertion)
+ Diag << Insertion.getValue();
+ }
+ return;
+ }
+
+ const auto *StdArrayDecl =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("type");
+
+ // For static arrays, this is handled in clang-diagnostic-array-bounds.
+ if (!StdArrayDecl)
+ return;
+
+ if (Index.isSigned() && Index.isNegative()) {
+ diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
+ << Index.toString(10);
+ return;
+ }
+
+ const TemplateArgumentList &TemplateArgs = StdArrayDecl->getTemplateArgs();
+ if (TemplateArgs.size() < 2)
+ return;
+ // First template arg of std::array is the type, second arg is the size.
+ const auto &SizeArg = TemplateArgs[1];
+ if (SizeArg.getKind() != TemplateArgument::Integral)
+ return;
+ llvm::APInt ArraySize = SizeArg.getAsIntegral();
+
+ // Get uint64_t values, because different bitwidths would lead to an assertion
+ // in APInt::uge.
+ if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
+ diag(Matched->getExprLoc(),
+ "std::array<> index %0 is past the end of the array "
+ "(which contains %1 elements)")
+ << Index.toString(10) << ArraySize.toString(10, false);
+ }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProBoundsConstantArrayIndexCheck.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_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This checks that all array subscriptions on static arrays and std::arrays
+/// have a constant index and are within bounds
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.html
+class ProBoundsConstantArrayIndexCheck : public ClangTidyCheck {
+ const std::string GslHeader;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+
+public:
+ ProBoundsConstantArrayIndexCheck(StringRef Name, ClangTidyContext *Context);
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
--- /dev/null
+//===--- ProBoundsPointerArithmeticCheck.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 "ProBoundsPointerArithmeticCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Flag all operators +, -, +=, -=, ++, -- that result in a pointer
+ Finder->addMatcher(
+ binaryOperator(
+ anyOf(hasOperatorName("+"), hasOperatorName("-"),
+ hasOperatorName("+="), hasOperatorName("-=")),
+ hasType(pointerType()),
+ unless(hasLHS(ignoringImpCasts(declRefExpr(to(isImplicit()))))))
+ .bind("expr"),
+ this);
+
+ Finder->addMatcher(
+ unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
+ hasType(pointerType()))
+ .bind("expr"),
+ this);
+
+ // Array subscript on a pointer (not an array) is also pointer arithmetic
+ Finder->addMatcher(
+ arraySubscriptExpr(
+ hasBase(ignoringImpCasts(
+ anyOf(hasType(pointerType()),
+ hasType(decayedType(hasDecayedType(pointerType())))))))
+ .bind("expr"),
+ this);
+}
+
+void ProBoundsPointerArithmeticCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>("expr");
+
+ diag(MatchedExpr->getExprLoc(), "do not use pointer arithmetic");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProBoundsPointerArithmeticCheck.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_PRO_BOUNDS_POINTER_ARITHMETIC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags all kinds of pointer arithmetic that have result of pointer type, i.e.
+/// +, -, +=, -=, ++, --. In addition, the [] operator on pointers (not on
+/// arrays) is flagged.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.html
+class ProBoundsPointerArithmeticCheck : public ClangTidyCheck {
+public:
+ ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
--- /dev/null
+//===--- ProTypeConstCastCheck.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 "ProTypeConstCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(cxxConstCastExpr().bind("cast"), this);
+}
+
+void ProTypeConstCastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCast = Result.Nodes.getNodeAs<CXXConstCastExpr>("cast");
+ diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeConstCastCheck.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_PRO_TYPE_CONST_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all instances of const_cast
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.html
+class ProTypeConstCastCheck : public ClangTidyCheck {
+public:
+ ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
--- /dev/null
+//===--- ProTypeCstyleCastCheck.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 "ProTypeCstyleCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+static bool needsConstCast(QualType SourceType, QualType DestType) {
+ SourceType = SourceType.getNonReferenceType();
+ DestType = DestType.getNonReferenceType();
+ while (SourceType->isPointerType() && DestType->isPointerType()) {
+ SourceType = SourceType->getPointeeType();
+ DestType = DestType->getPointeeType();
+ if (SourceType.isConstQualified() && !DestType.isConstQualified())
+ return true;
+ }
+ return false;
+}
+
+void ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this);
+}
+
+void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCast = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+
+ if (MatchedCast->getCastKind() == CK_BitCast ||
+ MatchedCast->getCastKind() == CK_LValueBitCast ||
+ MatchedCast->getCastKind() == CK_IntegralToPointer ||
+ MatchedCast->getCastKind() == CK_PointerToIntegral ||
+ MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) {
+ diag(MatchedCast->getLocStart(),
+ "do not use C-style cast to convert between unrelated types");
+ return;
+ }
+
+ QualType SourceType = MatchedCast->getSubExpr()->getType();
+
+ if (MatchedCast->getCastKind() == CK_BaseToDerived) {
+ const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
+ if (!SourceDecl) // The cast is from object to reference.
+ SourceDecl = SourceType->getAsCXXRecordDecl();
+ if (!SourceDecl)
+ return;
+
+ if (SourceDecl->isPolymorphic()) {
+ // Leave type spelling exactly as it was (unlike
+ // getTypeAsWritten().getAsString() which would spell enum types 'enum
+ // X').
+ StringRef DestTypeString = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ MatchedCast->getLParenLoc().getLocWithOffset(1),
+ MatchedCast->getRParenLoc().getLocWithOffset(-1)),
+ *Result.SourceManager, getLangOpts());
+
+ auto diag_builder = diag(
+ MatchedCast->getLocStart(),
+ "do not use C-style cast to downcast from a base to a derived class; "
+ "use dynamic_cast instead");
+
+ const Expr *SubExpr =
+ MatchedCast->getSubExprAsWritten()->IgnoreImpCasts();
+ std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str();
+ if (!isa<ParenExpr>(SubExpr)) {
+ CastText.push_back('(');
+ diag_builder << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0,
+ *Result.SourceManager, getLangOpts()),
+ ")");
+ }
+ auto ParenRange = CharSourceRange::getTokenRange(
+ MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc());
+ diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
+ } else {
+ diag(
+ MatchedCast->getLocStart(),
+ "do not use C-style cast to downcast from a base to a derived class");
+ }
+ return;
+ }
+
+ if (MatchedCast->getCastKind() == CK_NoOp &&
+ needsConstCast(SourceType, MatchedCast->getType())) {
+ diag(MatchedCast->getLocStart(),
+ "do not use C-style cast to cast away constness");
+ }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeCstyleCastCheck.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_PRO_TYPE_CSTYLE_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all use of C-style casts that perform a static_cast
+/// downcast, const_cast, or reinterpret_cast.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.html
+class ProTypeCstyleCastCheck : public ClangTidyCheck {
+public:
+ ProTypeCstyleCastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
--- /dev/null
+//===--- ProTypeMemberInitCheck.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 "ProTypeMemberInitCheck.h"
+#include "../utils/LexerUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+using llvm::SmallPtrSet;
+using llvm::SmallPtrSetImpl;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+
+AST_MATCHER(CXXRecordDecl, hasDefaultConstructor) {
+ return Node.hasDefaultConstructor();
+}
+
+// Iterate over all the fields in a record type, both direct and indirect (e.g.
+// if the record contains an anonmyous struct). If OneFieldPerUnion is true and
+// the record type (or indirect field) is a union, forEachField will stop after
+// the first field.
+template <typename T, typename Func>
+void forEachField(const RecordDecl &Record, const T &Fields,
+ bool OneFieldPerUnion, Func &&Fn) {
+ for (const FieldDecl *F : Fields) {
+ if (F->isAnonymousStructOrUnion()) {
+ if (const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl())
+ forEachField(*R, R->fields(), OneFieldPerUnion, Fn);
+ } else {
+ Fn(F);
+ }
+
+ if (OneFieldPerUnion && Record.isUnion())
+ break;
+ }
+}
+
+void removeFieldsInitializedInBody(
+ const Stmt &Stmt, ASTContext &Context,
+ SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
+ auto Matches =
+ match(findAll(binaryOperator(
+ hasOperatorName("="),
+ hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))),
+ Stmt, Context);
+ for (const auto &Match : Matches)
+ FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
+}
+
+StringRef getName(const FieldDecl *Field) { return Field->getName(); }
+
+StringRef getName(const RecordDecl *Record) {
+ // Get the typedef name if this is a C-style anonymous struct and typedef.
+ if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
+ return Typedef->getName();
+ return Record->getName();
+}
+
+// Creates comma separated list of decls requiring initialization in order of
+// declaration.
+template <typename R, typename T>
+std::string
+toCommaSeparatedString(const R &OrderedDecls,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ SmallVector<StringRef, 16> Names;
+ for (const T *Decl : OrderedDecls) {
+ if (DeclsToInit.count(Decl))
+ Names.emplace_back(getName(Decl));
+ }
+ return llvm::join(Names.begin(), Names.end(), ", ");
+}
+
+SourceLocation getLocationForEndOfToken(const ASTContext &Context,
+ SourceLocation Location) {
+ return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
+ Context.getLangOpts());
+}
+
+// There are 3 kinds of insertion placements:
+enum class InitializerPlacement {
+ // 1. The fields are inserted after an existing CXXCtorInitializer stored in
+ // Where. This will be the case whenever there is a written initializer before
+ // the fields available.
+ After,
+
+ // 2. The fields are inserted before the first existing initializer stored in
+ // Where.
+ Before,
+
+ // 3. There are no written initializers and the fields will be inserted before
+ // the constructor's body creating a new initializer list including the ':'.
+ New
+};
+
+// An InitializerInsertion contains a list of fields and/or base classes to
+// insert into the initializer list of a constructor. We use this to ensure
+// proper absolute ordering according to the class declaration relative to the
+// (perhaps improper) ordering in the existing initializer list, if any.
+struct IntializerInsertion {
+ IntializerInsertion(InitializerPlacement Placement,
+ const CXXCtorInitializer *Where)
+ : Placement(Placement), Where(Where) {}
+
+ SourceLocation getLocation(const ASTContext &Context,
+ const CXXConstructorDecl &Constructor) const {
+ assert((Where != nullptr || Placement == InitializerPlacement::New) &&
+ "Location should be relative to an existing initializer or this "
+ "insertion represents a new initializer list.");
+ SourceLocation Location;
+ switch (Placement) {
+ case InitializerPlacement::New:
+ Location = utils::lexer::getPreviousToken(
+ Context, Constructor.getBody()->getLocStart())
+ .getLocation();
+ break;
+ case InitializerPlacement::Before:
+ Location = utils::lexer::getPreviousToken(
+ Context, Where->getSourceRange().getBegin())
+ .getLocation();
+ break;
+ case InitializerPlacement::After:
+ Location = Where->getRParenLoc();
+ break;
+ }
+ return getLocationForEndOfToken(Context, Location);
+ }
+
+ std::string codeToInsert() const {
+ assert(!Initializers.empty() && "No initializers to insert");
+ std::string Code;
+ llvm::raw_string_ostream Stream(Code);
+ std::string joined =
+ llvm::join(Initializers.begin(), Initializers.end(), "(), ");
+ switch (Placement) {
+ case InitializerPlacement::New:
+ Stream << " : " << joined << "()";
+ break;
+ case InitializerPlacement::Before:
+ Stream << " " << joined << "(),";
+ break;
+ case InitializerPlacement::After:
+ Stream << ", " << joined << "()";
+ break;
+ }
+ return Stream.str();
+ }
+
+ InitializerPlacement Placement;
+ const CXXCtorInitializer *Where;
+ SmallVector<std::string, 4> Initializers;
+};
+
+// Convenience utility to get a RecordDecl from a QualType.
+const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
+ if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
+ return RT->getDecl();
+ return nullptr;
+}
+
+template <typename R, typename T>
+SmallVector<IntializerInsertion, 16>
+computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
+ const R &OrderedDecls,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ SmallVector<IntializerInsertion, 16> Insertions;
+ Insertions.emplace_back(InitializerPlacement::New, nullptr);
+
+ typename R::const_iterator Decl = std::begin(OrderedDecls);
+ for (const CXXCtorInitializer *Init : Inits) {
+ if (Init->isWritten()) {
+ if (Insertions.size() == 1)
+ Insertions.emplace_back(InitializerPlacement::Before, Init);
+
+ // Gets either the field or base class being initialized by the provided
+ // initializer.
+ const auto *InitDecl =
+ Init->isAnyMemberInitializer()
+ ? static_cast<const NamedDecl *>(Init->getAnyMember())
+ : Init->getBaseClass()->getAsCXXRecordDecl();
+
+ // Add all fields between current field up until the next intializer.
+ for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
+ if (const auto *D = dyn_cast<T>(*Decl)) {
+ if (DeclsToInit.count(D) > 0)
+ Insertions.back().Initializers.emplace_back(getName(D));
+ }
+ }
+
+ Insertions.emplace_back(InitializerPlacement::After, Init);
+ }
+ }
+
+ // Add remaining decls that require initialization.
+ for (; Decl != std::end(OrderedDecls); ++Decl) {
+ if (const auto *D = dyn_cast<T>(*Decl)) {
+ if (DeclsToInit.count(D) > 0)
+ Insertions.back().Initializers.emplace_back(getName(D));
+ }
+ }
+ return Insertions;
+}
+
+// Gets the list of bases and members that could possibly be initialized, in
+// order as they appear in the class declaration.
+void getInitializationsInOrder(const CXXRecordDecl &ClassDecl,
+ SmallVectorImpl<const NamedDecl *> &Decls) {
+ Decls.clear();
+ for (const auto &Base : ClassDecl.bases()) {
+ // Decl may be null if the base class is a template parameter.
+ if (const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
+ Decls.emplace_back(Decl);
+ }
+ }
+ forEachField(ClassDecl, ClassDecl.fields(), false,
+ [&](const FieldDecl *F) { Decls.push_back(F); });
+}
+
+template <typename T>
+void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
+ const CXXConstructorDecl *Ctor,
+ const SmallPtrSetImpl<const T *> &DeclsToInit) {
+ // Do not propose fixes in macros since we cannot place them correctly.
+ if (Ctor->getLocStart().isMacroID())
+ return;
+
+ SmallVector<const NamedDecl *, 16> OrderedDecls;
+ getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
+
+ for (const auto &Insertion :
+ computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
+ if (!Insertion.Initializers.empty())
+ Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
+ Insertion.codeToInsert());
+ }
+}
+
+} // anonymous namespace
+
+ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreArrays(Options.get("IgnoreArrays", false)) {}
+
+void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto IsUserProvidedNonDelegatingConstructor =
+ allOf(isUserProvided(),
+ unless(anyOf(isInstantiated(), isDelegatingConstructor())));
+ auto IsNonTrivialDefaultConstructor = allOf(
+ isDefaultConstructor(), unless(isUserProvided()),
+ hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
+ Finder->addMatcher(
+ cxxConstructorDecl(isDefinition(),
+ anyOf(IsUserProvidedNonDelegatingConstructor,
+ IsNonTrivialDefaultConstructor))
+ .bind("ctor"),
+ this);
+
+ // Match classes with a default constructor that is defaulted or is not in the
+ // AST.
+ Finder->addMatcher(
+ cxxRecordDecl(
+ isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
+ anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
+ unless(isImplicit()))),
+ unless(has(cxxConstructorDecl()))),
+ unless(isTriviallyDefaultConstructible()))
+ .bind("record"),
+ this);
+
+ auto HasDefaultConstructor = hasInitializer(
+ cxxConstructExpr(unless(requiresZeroInitialization()),
+ hasDeclaration(cxxConstructorDecl(
+ isDefaultConstructor(), unless(isUserProvided())))));
+ Finder->addMatcher(
+ varDecl(isDefinition(), HasDefaultConstructor,
+ hasAutomaticStorageDuration(),
+ hasType(recordDecl(has(fieldDecl()),
+ isTriviallyDefaultConstructible())))
+ .bind("var"),
+ this);
+}
+
+void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
+ // Skip declarations delayed by late template parsing without a body.
+ if (!Ctor->getBody())
+ return;
+ checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
+ checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
+ } else if (const auto *Record =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("record")) {
+ assert(Record->hasDefaultConstructor() &&
+ "Matched record should have a default constructor");
+ checkMissingMemberInitializer(*Result.Context, *Record, nullptr);
+ checkMissingBaseClassInitializer(*Result.Context, *Record, nullptr);
+ } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
+ checkUninitializedTrivialType(*Result.Context, Var);
+ }
+}
+
+void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreArrays", IgnoreArrays);
+}
+
+// FIXME: Copied from clang/lib/Sema/SemaDeclCXX.cpp.
+static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T) {
+ if (T->isIncompleteArrayType())
+ return true;
+
+ while (const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
+ if (!ArrayT->getSize())
+ return true;
+
+ T = ArrayT->getElementType();
+ }
+
+ return false;
+}
+
+static bool isEmpty(ASTContext &Context, const QualType &Type) {
+ if (const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
+ return ClassDecl->isEmpty();
+ }
+ return isIncompleteOrZeroLengthArrayType(Context, Type);
+}
+
+void ProTypeMemberInitCheck::checkMissingMemberInitializer(
+ ASTContext &Context, const CXXRecordDecl &ClassDecl,
+ const CXXConstructorDecl *Ctor) {
+ bool IsUnion = ClassDecl.isUnion();
+
+ if (IsUnion && ClassDecl.hasInClassInitializer())
+ return;
+
+ // Gather all fields (direct and indirect) that need to be initialized.
+ SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
+ forEachField(ClassDecl, ClassDecl.fields(), false, [&](const FieldDecl *F) {
+ if (!F->hasInClassInitializer() &&
+ utils::type_traits::isTriviallyDefaultConstructible(F->getType(),
+ Context) &&
+ !isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
+ FieldsToInit.insert(F);
+ });
+ if (FieldsToInit.empty())
+ return;
+
+ if (Ctor) {
+ for (const CXXCtorInitializer *Init : Ctor->inits()) {
+ // Remove any fields that were explicitly written in the initializer list
+ // or in-class.
+ if (Init->isAnyMemberInitializer() && Init->isWritten()) {
+ if (IsUnion)
+ return; // We can only initialize one member of a union.
+ FieldsToInit.erase(Init->getAnyMember());
+ }
+ }
+ removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
+ }
+
+ // Collect all fields in order, both direct fields and indirect fields from
+ // anonmyous record types.
+ SmallVector<const FieldDecl *, 16> OrderedFields;
+ forEachField(ClassDecl, ClassDecl.fields(), false,
+ [&](const FieldDecl *F) { OrderedFields.push_back(F); });
+
+ // Collect all the fields we need to initialize, including indirect fields.
+ SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
+ forEachField(ClassDecl, FieldsToInit, false,
+ [&](const FieldDecl *F) { AllFieldsToInit.insert(F); });
+ if (AllFieldsToInit.empty())
+ return;
+
+ DiagnosticBuilder Diag =
+ diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
+ IsUnion
+ ? "union constructor should initialize one of these fields: %0"
+ : "constructor does not initialize these fields: %0")
+ << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
+
+ // Do not propose fixes for constructors in macros since we cannot place them
+ // correctly.
+ if (Ctor && Ctor->getLocStart().isMacroID())
+ return;
+
+ // Collect all fields but only suggest a fix for the first member of unions,
+ // as initializing more than one union member is an error.
+ SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
+ forEachField(ClassDecl, FieldsToInit, true, [&](const FieldDecl *F) {
+ // Don't suggest fixes for enums because we don't know a good default.
+ // Don't suggest fixes for bitfields because in-class initialization is not
+ // possible.
+ if (!F->getType()->isEnumeralType() && !F->isBitField())
+ FieldsToFix.insert(F);
+ });
+ if (FieldsToFix.empty())
+ return;
+
+ // Use in-class initialization if possible.
+ if (Context.getLangOpts().CPlusPlus11) {
+ for (const FieldDecl *Field : FieldsToFix) {
+ Diag << FixItHint::CreateInsertion(
+ getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
+ "{}");
+ }
+ } else if (Ctor) {
+ // Otherwise, rewrite the constructor's initializer list.
+ fixInitializerList(Context, Diag, Ctor, FieldsToFix);
+ }
+}
+
+void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
+ const ASTContext &Context, const CXXRecordDecl &ClassDecl,
+ const CXXConstructorDecl *Ctor) {
+
+ // Gather any base classes that need to be initialized.
+ SmallVector<const RecordDecl *, 4> AllBases;
+ SmallPtrSet<const RecordDecl *, 4> BasesToInit;
+ for (const CXXBaseSpecifier &Base : ClassDecl.bases()) {
+ if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
+ AllBases.emplace_back(BaseClassDecl);
+ if (!BaseClassDecl->field_empty() &&
+ utils::type_traits::isTriviallyDefaultConstructible(Base.getType(),
+ Context))
+ BasesToInit.insert(BaseClassDecl);
+ }
+ }
+
+ if (BasesToInit.empty())
+ return;
+
+ // Remove any bases that were explicitly written in the initializer list.
+ if (Ctor) {
+ if (Ctor->isImplicit())
+ return;
+
+ for (const CXXCtorInitializer *Init : Ctor->inits()) {
+ if (Init->isBaseInitializer() && Init->isWritten())
+ BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
+ }
+ }
+
+ if (BasesToInit.empty())
+ return;
+
+ DiagnosticBuilder Diag =
+ diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
+ "constructor does not initialize these bases: %0")
+ << toCommaSeparatedString(AllBases, BasesToInit);
+
+ if (Ctor)
+ fixInitializerList(Context, Diag, Ctor, BasesToInit);
+}
+
+void ProTypeMemberInitCheck::checkUninitializedTrivialType(
+ const ASTContext &Context, const VarDecl *Var) {
+ DiagnosticBuilder Diag =
+ diag(Var->getLocStart(), "uninitialized record type: %0") << Var;
+
+ Diag << FixItHint::CreateInsertion(
+ getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
+ Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeMemberInitCheck.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_PRO_TYPE_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// \brief Implements C++ Core Guidelines Type.6.
+///
+/// Checks that every user-provided constructor value-initializes all class
+/// members and base classes that would have undefined behavior otherwise. Also
+/// check that any record types without user-provided default constructors are
+/// value-initialized where used.
+///
+/// Members initialized through function calls in the body of the constructor
+/// will result in false positives.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html
+/// TODO: See if 'fixes' for false positives are optimized away by the compiler.
+/// TODO: For classes with multiple constructors, make sure that we don't offer
+/// multiple in-class initializer fixits for the same member.
+class ProTypeMemberInitCheck : public ClangTidyCheck {
+public:
+ ProTypeMemberInitCheck(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:
+ // Checks Type.6 part 1:
+ // Issue a diagnostic for any constructor of a non-trivially-constructible
+ // type that does not initialize all member variables.
+ //
+ // To fix: Write a data member initializer, or mention it in the member
+ // initializer list.
+ void checkMissingMemberInitializer(ASTContext &Context,
+ const CXXRecordDecl &ClassDecl,
+ const CXXConstructorDecl *Ctor);
+
+ // A subtle side effect of Type.6 part 2:
+ // Make sure to initialize trivially constructible base classes.
+ void checkMissingBaseClassInitializer(const ASTContext &Context,
+ const CXXRecordDecl &ClassDecl,
+ const CXXConstructorDecl *Ctor);
+
+ // Checks Type.6 part 2:
+ // Issue a diagnostic when constructing an object of a trivially constructible
+ // type without () or {} to initialize its members.
+ //
+ // To fix: Add () or {}.
+ void checkUninitializedTrivialType(const ASTContext &Context,
+ const VarDecl *Var);
+
+ // Whether arrays need to be initialized or not. Default is false.
+ bool IgnoreArrays;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
--- /dev/null
+//===--- ProTypeReinterpretCastCheck.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 "ProTypeReinterpretCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeReinterpretCastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(cxxReinterpretCastExpr().bind("cast"), this);
+}
+
+void ProTypeReinterpretCastCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCast =
+ Result.Nodes.getNodeAs<CXXReinterpretCastExpr>("cast");
+ diag(MatchedCast->getOperatorLoc(), "do not use reinterpret_cast");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeReinterpretCast.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_PRO_TYPE_REINTERPRETCAST_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_REINTERPRETCAST_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags all occurrences of reinterpret_cast
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.html
+class ProTypeReinterpretCastCheck : public ClangTidyCheck {
+public:
+ ProTypeReinterpretCastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_REINTERPRETCAST_CHECK_H
--- /dev/null
+//===--- ProTypeStaticCastDowncastCheck.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 "ProTypeStaticCastDowncastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeStaticCastDowncastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cxxStaticCastExpr(unless(isInTemplateInstantiation())).bind("cast"),
+ this);
+}
+
+void ProTypeStaticCastDowncastCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *MatchedCast = Result.Nodes.getNodeAs<CXXStaticCastExpr>("cast");
+ if (MatchedCast->getCastKind() != CK_BaseToDerived)
+ return;
+
+ QualType SourceType = MatchedCast->getSubExpr()->getType();
+ const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
+ if (!SourceDecl) // The cast is from object to reference
+ SourceDecl = SourceType->getAsCXXRecordDecl();
+ if (!SourceDecl)
+ return;
+
+ if (SourceDecl->isPolymorphic())
+ diag(MatchedCast->getOperatorLoc(),
+ "do not use static_cast to downcast from a base to a derived class; "
+ "use dynamic_cast instead")
+ << FixItHint::CreateReplacement(MatchedCast->getOperatorLoc(),
+ "dynamic_cast");
+ else
+ diag(MatchedCast->getOperatorLoc(),
+ "do not use static_cast to downcast from a base to a derived class");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeStaticCastDowncastCheck.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_PRO_TYPE_STATIC_CAST_DOWNCAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H
+
+#include "../ClangTidy.h"
+
+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:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.html
+class ProTypeStaticCastDowncastCheck : public ClangTidyCheck {
+public:
+ ProTypeStaticCastDowncastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H
--- /dev/null
+//===--- ProTypeUnionAccessCheck.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 "ProTypeUnionAccessCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ memberExpr(hasObjectExpression(hasType(recordDecl(isUnion()))))
+ .bind("expr"),
+ this);
+}
+
+void ProTypeUnionAccessCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Matched = Result.Nodes.getNodeAs<MemberExpr>("expr");
+ diag(Matched->getMemberLoc(),
+ "do not access members of unions; use (boost::)variant instead");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeUnionAccessCheck.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_PRO_TYPE_UNION_ACCESS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_UNION_ACCESS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all access to members of unions.
+/// Access to a union as a whole (e.g. passing to a function) is not flagged.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.html
+class ProTypeUnionAccessCheck : public ClangTidyCheck {
+public:
+ ProTypeUnionAccessCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_UNION_ACCESS_H
--- /dev/null
+//===--- ProTypeVarargCheck.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 "ProTypeVarargCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+const internal::VariadicDynCastAllOfMatcher<Stmt, VAArgExpr> vAArgExpr;
+
+void ProTypeVarargCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(vAArgExpr().bind("va_use"), this);
+
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(isVariadic()))).bind("callvararg"), this);
+}
+
+static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I) {
+ const auto *FDecl = dyn_cast<FunctionDecl>(C->getCalleeDecl());
+ if (!FDecl)
+ return false;
+
+ auto N = FDecl->getNumParams(); // Number of parameters without '...'
+ if (C->getNumArgs() != N + 1)
+ return false; // more/less than one argument passed to '...'
+
+ const auto *IntLit =
+ dyn_cast<IntegerLiteral>(C->getArg(N)->IgnoreParenImpCasts());
+ if (!IntLit)
+ return false;
+
+ if (IntLit->getValue() != I)
+ return false;
+
+ return true;
+}
+
+void ProTypeVarargCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("callvararg")) {
+ if (hasSingleVariadicArgumentWithValue(Matched, 0))
+ return;
+ diag(Matched->getExprLoc(), "do not call c-style vararg functions");
+ }
+
+ if (const auto *Matched = Result.Nodes.getNodeAs<Expr>("va_use")) {
+ diag(Matched->getExprLoc(),
+ "do not use va_start/va_arg to define c-style vararg functions; "
+ "use variadic templates instead");
+ }
+
+ if (const auto *Matched = Result.Nodes.getNodeAs<VarDecl>("va_list")) {
+ auto SR = Matched->getSourceRange();
+ if (SR.isInvalid())
+ return; // some implicitly generated builtins take va_list
+ diag(SR.getBegin(), "do not declare variables of type va_list; "
+ "use variadic templates instead");
+ }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ProTypeVarargCheck.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_PRO_TYPE_VARARG_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_VARARG_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all calls to c-style variadic functions and all use
+/// of va_arg.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.html
+class ProTypeVarargCheck : public ClangTidyCheck {
+public:
+ ProTypeVarargCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_VARARG_H
--- /dev/null
+//===--- SlicingCheck.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 "SlicingCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void SlicingCheck::registerMatchers(MatchFinder *Finder) {
+ // When we see:
+ // class B : public A { ... };
+ // A a;
+ // B b;
+ // a = b;
+ // The assignment is OK if:
+ // - the assignment operator is defined as taking a B as second parameter,
+ // or
+ // - B does not define any additional members (either variables or
+ // overrides) wrt A.
+ //
+ // The same holds for copy ctor calls. This also captures stuff like:
+ // void f(A a);
+ // f(b);
+
+ // Helpers.
+ const auto OfBaseClass = ofClass(cxxRecordDecl().bind("BaseDecl"));
+ const auto IsDerivedFromBaseDecl =
+ cxxRecordDecl(isDerivedFrom(equalsBoundNode("BaseDecl")))
+ .bind("DerivedDecl");
+ const auto HasTypeDerivedFromBaseDecl =
+ anyOf(hasType(IsDerivedFromBaseDecl),
+ hasType(references(IsDerivedFromBaseDecl)));
+ const auto IsWithinDerivedCtor =
+ hasParent(cxxConstructorDecl(ofClass(equalsBoundNode("DerivedDecl"))));
+
+ // Assignement slicing: "a = b;" and "a = std::move(b);" variants.
+ const auto SlicesObjectInAssignment =
+ callExpr(callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(),
+ isMoveAssignmentOperator()),
+ OfBaseClass)),
+ hasArgument(1, HasTypeDerivedFromBaseDecl));
+
+ // Construction slicing: "A a{b};" and "f(b);" variants. Note that in case of
+ // slicing the letter will create a temporary and therefore call a ctor.
+ const auto SlicesObjectInCtor = cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(
+ anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)),
+ hasArgument(0, HasTypeDerivedFromBaseDecl),
+ // We need to disable matching on the call to the base copy/move
+ // constructor in DerivedDecl's constructors.
+ unless(IsWithinDerivedCtor));
+
+ Finder->addMatcher(
+ expr(anyOf(SlicesObjectInAssignment, SlicesObjectInCtor)).bind("Call"),
+ this);
+}
+
+/// Warns on methods overridden in DerivedDecl with respect to BaseDecl.
+/// FIXME: this warns on all overrides outside of the sliced path in case of
+/// multiple inheritance.
+void SlicingCheck::DiagnoseSlicedOverriddenMethods(
+ const Expr &Call, const CXXRecordDecl &DerivedDecl,
+ const CXXRecordDecl &BaseDecl) {
+ if (DerivedDecl.getCanonicalDecl() == BaseDecl.getCanonicalDecl())
+ return;
+ for (const auto &Method : DerivedDecl.methods()) {
+ // Virtual destructors are OK. We're ignoring constructors since they are
+ // tagged as overrides.
+ if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
+ continue;
+ if (Method->size_overridden_methods() > 0) {
+ diag(Call.getExprLoc(),
+ "slicing object from type %0 to %1 discards override %2")
+ << &DerivedDecl << &BaseDecl << Method;
+ }
+ }
+ // Recursively process bases.
+ for (const auto &Base : DerivedDecl.bases()) {
+ if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) {
+ if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
+ BaseRecordType->getDecl()->getDefinition()))
+ DiagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
+ }
+ }
+}
+
+void SlicingCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *BaseDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("BaseDecl");
+ const auto *DerivedDecl =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("DerivedDecl");
+ const auto *Call = Result.Nodes.getNodeAs<Expr>("Call");
+ assert(BaseDecl != nullptr);
+ assert(DerivedDecl != nullptr);
+ assert(Call != nullptr);
+
+ // Warn when slicing the vtable.
+ // We're looking through all the methods in the derived class and see if they
+ // override some methods in the base class.
+ // It's not enough to just test whether the class is polymorphic because we
+ // would be fine slicing B to A if no method in B (or its bases) overrides
+ // anything in A:
+ // class A { virtual void f(); };
+ // class B : public A {};
+ // because in that case calling A::f is the same as calling B::f.
+ DiagnoseSlicedOverriddenMethods(*Call, *DerivedDecl, *BaseDecl);
+
+ // Warn when slicing member variables.
+ const auto &BaseLayout =
+ BaseDecl->getASTContext().getASTRecordLayout(BaseDecl);
+ const auto &DerivedLayout =
+ DerivedDecl->getASTContext().getASTRecordLayout(DerivedDecl);
+ const CharUnits StateSize =
+ DerivedLayout.getDataSize() - BaseLayout.getDataSize();
+ if (StateSize.isPositive()) {
+ diag(Call->getExprLoc(), "slicing object from type %0 to %1 discards "
+ "%2 bytes of state")
+ << DerivedDecl << BaseDecl << static_cast<int>(StateSize.getQuantity());
+ }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SlicingCheck.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_SLICING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SLICING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags slicing (incomplete copying of an object's state) of member variables
+/// or vtable. See:
+/// - https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es63-dont-slice
+/// for the former, and
+/// - https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c145-access-polymorphic-objects-through-pointers-and-references
+/// for the latter
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-slicing.html
+class SlicingCheck : public ClangTidyCheck {
+public:
+ SlicingCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void DiagnoseSlicedOverriddenMethods(const Expr &call,
+ const CXXRecordDecl &DerivedDecl,
+ const CXXRecordDecl &BaseDecl);
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SLICING_H
--- /dev/null
+//===--- 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 {
+
+SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
+ AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
+
+void SpecialMemberFunctionsCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
+ Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
+}
+
+void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ 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::
+ DefaultDestructor:
+ return "a default destructor";
+ case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
+ NonDefaultDestructor:
+ return "a non-default destructor";
+ case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
+ return "a copy constructor";
+ case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
+ 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<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> 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 auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
+ if (!MatchedDecl)
+ return;
+
+ ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
+
+ auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) {
+ llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
+ ClassWithSpecialMembers[ID];
+ if (!llvm::is_contained(Members, Kind))
+ Members.push_back(Kind);
+ };
+
+ if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {
+ StoreMember(Dtor->isDefaulted()
+ ? SpecialMemberFunctionKind::DefaultDestructor
+ : SpecialMemberFunctionKind::NonDefaultDestructor);
+ }
+
+ std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
+ Matchers = {{"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<CXXMethodDecl>(KV.first)) {
+ StoreMember(KV.second);
+ }
+}
+
+void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
+ for (const auto &C : ClassWithSpecialMembers) {
+ checkForMissingMembers(C.first, C.second);
+ }
+}
+
+void SpecialMemberFunctionsCheck::checkForMissingMembers(
+ const ClassDefId &ID,
+ llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) {
+ llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
+
+ auto HasMember = [&](SpecialMemberFunctionKind Kind) {
+ return llvm::is_contained(DefinedMembers, Kind);
+ };
+
+ auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
+ if (!HasMember(Kind))
+ MissingMembers.push_back(Kind);
+ };
+
+ bool RequireThree =
+ HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) ||
+ (!AllowSoleDefaultDtor &&
+ HasMember(SpecialMemberFunctionKind::DefaultDestructor)) ||
+ HasMember(SpecialMemberFunctionKind::CopyConstructor) ||
+ HasMember(SpecialMemberFunctionKind::CopyAssignment) ||
+ HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
+ HasMember(SpecialMemberFunctionKind::MoveAssignment);
+
+ bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
+ getLangOpts().CPlusPlus11) ||
+ HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
+ HasMember(SpecialMemberFunctionKind::MoveAssignment);
+
+ if (RequireThree) {
+ if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) &&
+ !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))
+ MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
+
+ RequireMember(SpecialMemberFunctionKind::CopyConstructor);
+ RequireMember(SpecialMemberFunctionKind::CopyAssignment);
+ }
+
+ if (RequireFive) {
+ assert(RequireThree);
+ RequireMember(SpecialMemberFunctionKind::MoveConstructor);
+ RequireMember(SpecialMemberFunctionKind::MoveAssignment);
+ }
+
+ if (!MissingMembers.empty())
+ diag(ID.first, "class '%0' defines %1 but does not define %2")
+ << ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
+ << cppcoreguidelines::join(MissingMembers, " or ");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- 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);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onEndOfTranslationUnit() override;
+
+ enum class SpecialMemberFunctionKind : uint8_t {
+ Destructor,
+ DefaultDestructor,
+ NonDefaultDestructor,
+ CopyConstructor,
+ CopyAssignment,
+ MoveConstructor,
+ MoveAssignment
+ };
+
+ using ClassDefId = std::pair<SourceLocation, std::string>;
+
+ using ClassDefiningSpecialMembersMap =
+ llvm::DenseMap<ClassDefId,
+ llvm::SmallVector<SpecialMemberFunctionKind, 5>>;
+
+private:
+ void checkForMissingMembers(
+ const ClassDefId &ID,
+ llvm::ArrayRef<SpecialMemberFunctionKind> DefinedSpecialMembers);
+
+ const bool AllowMissingMoveFunctions;
+ const bool AllowSoleDefaultDtor;
+ 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<unsigned>(-1)),
+ "EMPTY");
+ }
+
+ static inline ClassDefId getTombstoneKey() {
+ return ClassDefId(
+ clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-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<ClassDefId::second_type> SecondHash;
+ return Val.first.getRawEncoding() + SecondHash(Val.second);
+ }
+
+ static bool isEqual(const ClassDefId &LHS, const 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
--- /dev/null
+//===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidCStyleCastsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+void AvoidCStyleCastsCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(
+ cStyleCastExpr(
+ // Filter out (EnumType)IntegerLiteral construct, which is generated
+ // for non-type template arguments of enum types.
+ // FIXME: Remove this once this is fixed in the AST.
+ unless(hasParent(substNonTypeTemplateParmExpr())),
+ // Avoid matches in template instantiations.
+ unless(isInTemplateInstantiation()))
+ .bind("cast"),
+ this);
+}
+
+static bool needsConstCast(QualType SourceType, QualType DestType) {
+ while ((SourceType->isPointerType() && DestType->isPointerType()) ||
+ (SourceType->isReferenceType() && DestType->isReferenceType())) {
+ SourceType = SourceType->getPointeeType();
+ DestType = DestType->getPointeeType();
+ if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
+ return (SourceType->isPointerType() == DestType->isPointerType()) &&
+ (SourceType->isReferenceType() == DestType->isReferenceType());
+ }
+ }
+ return false;
+}
+
+static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
+ while ((T1->isPointerType() && T2->isPointerType()) ||
+ (T1->isReferenceType() && T2->isReferenceType())) {
+ T1 = T1->getPointeeType();
+ T2 = T2->getPointeeType();
+ }
+ return T1.getUnqualifiedType() == T2.getUnqualifiedType();
+}
+
+void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+
+ // Ignore casts in macros.
+ if (CastExpr->getExprLoc().isMacroID())
+ return;
+
+ // Casting to void is an idiomatic way to mute "unused variable" and similar
+ // warnings.
+ if (CastExpr->getCastKind() == CK_ToVoid)
+ return;
+
+ auto isFunction = [](QualType T) {
+ T = T.getCanonicalType().getNonReferenceType();
+ return T->isFunctionType() || T->isFunctionPointerType() ||
+ T->isMemberFunctionPointerType();
+ };
+
+ const QualType DestTypeAsWritten =
+ CastExpr->getTypeAsWritten().getUnqualifiedType();
+ const QualType SourceTypeAsWritten =
+ CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
+ const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
+ const QualType DestType = DestTypeAsWritten.getCanonicalType();
+
+ auto ReplaceRange = CharSourceRange::getCharRange(
+ CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getLocStart());
+
+ bool FnToFnCast =
+ isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
+
+ if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
+ // Function pointer/reference casts may be needed to resolve ambiguities in
+ // case of overloaded functions, so detection of redundant casts is trickier
+ // in this case. Don't emit "redundant cast" warnings for function
+ // pointer/reference types.
+ if (SourceTypeAsWritten == DestTypeAsWritten) {
+ diag(CastExpr->getLocStart(), "redundant cast to the same type")
+ << FixItHint::CreateRemoval(ReplaceRange);
+ return;
+ }
+ }
+
+ // The rest of this check is only relevant to C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+ // Ignore code inside extern "C" {} blocks.
+ if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
+ .empty())
+ return;
+ // Ignore code in .c files and headers included from them, even if they are
+ // compiled as C++.
+ if (getCurrentMainFile().endswith(".c"))
+ return;
+
+ SourceManager &SM = *Result.SourceManager;
+
+ // Ignore code in .c files #included in other files (which shouldn't be done,
+ // but people still do this for test and other purposes).
+ if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
+ return;
+
+ // Leave type spelling exactly as it was (unlike
+ // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
+ StringRef DestTypeString =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ CastExpr->getLParenLoc().getLocWithOffset(1),
+ CastExpr->getRParenLoc().getLocWithOffset(-1)),
+ SM, getLangOpts());
+
+ auto Diag =
+ diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
+
+ auto ReplaceWithCast = [&](std::string CastText) {
+ const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
+ if (!isa<ParenExpr>(SubExpr)) {
+ CastText.push_back('(');
+ Diag << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
+ getLangOpts()),
+ ")");
+ }
+ Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
+ };
+ auto ReplaceWithNamedCast = [&](StringRef CastType) {
+ Diag << CastType;
+ ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
+ };
+
+ // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
+ switch (CastExpr->getCastKind()) {
+ case CK_FunctionToPointerDecay:
+ ReplaceWithNamedCast("static_cast");
+ return;
+ case CK_ConstructorConversion:
+ if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
+ DestTypeAsWritten->isRecordType() &&
+ !DestTypeAsWritten->isElaboratedTypeSpecifier()) {
+ Diag << "constructor call syntax";
+ // FIXME: Validate DestTypeString, maybe.
+ ReplaceWithCast(DestTypeString.str());
+ } else {
+ ReplaceWithNamedCast("static_cast");
+ }
+ return;
+ case CK_NoOp:
+ if (FnToFnCast) {
+ ReplaceWithNamedCast("static_cast");
+ return;
+ }
+ if (SourceType == DestType) {
+ Diag << "static_cast (if needed, the cast may be redundant)";
+ ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
+ return;
+ }
+ if (needsConstCast(SourceType, DestType) &&
+ pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
+ ReplaceWithNamedCast("const_cast");
+ return;
+ }
+ if (DestType->isReferenceType()) {
+ QualType Dest = DestType.getNonReferenceType();
+ QualType Source = SourceType.getNonReferenceType();
+ if (Source == Dest.withConst() ||
+ SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
+ ReplaceWithNamedCast("const_cast");
+ return;
+ }
+ break;
+ }
+ // FALLTHROUGH
+ case clang::CK_IntegralCast:
+ // Convert integral and no-op casts between builtin types and enums to
+ // static_cast. A cast from enum to integer may be unnecessary, but it's
+ // still retained.
+ if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
+ (DestType->isBuiltinType() || DestType->isEnumeralType())) {
+ ReplaceWithNamedCast("static_cast");
+ return;
+ }
+ break;
+ case CK_BitCast:
+ // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
+ if (!needsConstCast(SourceType, DestType)) {
+ if (SourceType->isVoidPointerType())
+ ReplaceWithNamedCast("static_cast");
+ else
+ ReplaceWithNamedCast("reinterpret_cast");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ Diag << "static_cast/const_cast/reinterpret_cast";
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- AvoidCStyleCastsCheck.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_GOOGLE_AVOIDCSTYLECASTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_AVOIDCSTYLECASTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Finds usages of C-style casts.
+///
+/// https://google.github.io/styleguide/cppguide.html#Casting
+///
+/// Corresponding cpplint.py check name: 'readability/casting'.
+///
+/// This check is similar to `-Wold-style-cast`, but it suggests automated fixes
+/// in some cases. The reported locations should not be different from the
+/// ones generated by `-Wold-style-cast`.
+class AvoidCStyleCastsCheck : public ClangTidyCheck {
+public:
+ AvoidCStyleCastsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_AVOIDCSTYLECASTSCHECK_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyGoogleModule
+ AvoidCStyleCastsCheck.cpp
+ DefaultArgumentsCheck.cpp
+ ExplicitConstructorCheck.cpp
+ ExplicitMakePairCheck.cpp
+ GlobalNamesInHeadersCheck.cpp
+ GoogleTidyModule.cpp
+ IntegerTypesCheck.cpp
+ NonConstReferences.cpp
+ OverloadedUnaryAndCheck.cpp
+ StringReferenceMemberCheck.cpp
+ TodoCommentCheck.cpp
+ UnnamedNamespaceInHeaderCheck.cpp
+ UsingNamespaceDirectiveCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyReadabilityModule
+ clangTidyUtils
+ )
--- /dev/null
+//===--- DefaultArgumentsCheck.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 "DefaultArgumentsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+void DefaultArgumentsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ cxxMethodDecl(anyOf(isOverride(), isVirtual()),
+ hasAnyParameter(parmVarDecl(hasInitializer(expr()))))
+ .bind("Decl"),
+ this);
+}
+
+void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("Decl");
+ diag(MatchedDecl->getLocation(),
+ "default arguments on virtual or override methods are prohibited");
+}
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DefaultArgumentsCheck.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_GOOGLE_DEFAULT_ARGUMENTS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_DEFAULT_ARGUMENTS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+/// Checks that default parameters are not given for virtual methods.
+///
+/// See https://google.github.io/styleguide/cppguide.html#Default_Arguments
+class DefaultArgumentsCheck : public ClangTidyCheck {
+public:
+ DefaultArgumentsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_DEFAULT_ARGUMENTS_H
--- /dev/null
+//===--- ExplicitConstructorCheck.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 "ExplicitConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+ Finder->addMatcher(
+ cxxConstructorDecl(unless(anyOf(isImplicit(), // Compiler-generated.
+ isDeleted(), isInstantiated())))
+ .bind("ctor"),
+ this);
+ Finder->addMatcher(
+ cxxConversionDecl(unless(anyOf(isExplicit(), // Already marked explicit.
+ isImplicit(), // Compiler-generated.
+ isDeleted(), isInstantiated())))
+
+ .bind("conversion"),
+ this);
+}
+
+// Looks for the token matching the predicate and returns the range of the found
+// token including trailing whitespace.
+static SourceRange FindToken(const SourceManager &Sources,
+ const LangOptions &LangOpts,
+ SourceLocation StartLoc, SourceLocation EndLoc,
+ bool (*Pred)(const Token &)) {
+ if (StartLoc.isMacroID() || EndLoc.isMacroID())
+ return SourceRange();
+ FileID File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));
+ StringRef Buf = Sources.getBufferData(File);
+ const char *StartChar = Sources.getCharacterData(StartLoc);
+ Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());
+ Lex.SetCommentRetentionState(true);
+ Token Tok;
+ do {
+ Lex.LexFromRawLexer(Tok);
+ if (Pred(Tok)) {
+ Token NextTok;
+ Lex.LexFromRawLexer(NextTok);
+ return SourceRange(Tok.getLocation(), NextTok.getLocation());
+ }
+ } while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);
+
+ return SourceRange();
+}
+
+static bool declIsStdInitializerList(const NamedDecl *D) {
+ // First use the fast getName() method to avoid unnecessary calls to the
+ // slow getQualifiedNameAsString().
+ return D->getName() == "initializer_list" &&
+ D->getQualifiedNameAsString() == "std::initializer_list";
+}
+
+static bool isStdInitializerList(QualType Type) {
+ Type = Type.getCanonicalType();
+ if (const auto *TS = Type->getAs<TemplateSpecializationType>()) {
+ if (const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())
+ return declIsStdInitializerList(TD);
+ }
+ if (const auto *RT = Type->getAs<RecordType>()) {
+ if (const auto *Specialization =
+ dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))
+ return declIsStdInitializerList(Specialization->getSpecializedTemplate());
+ }
+ return false;
+}
+
+void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
+ constexpr char WarningMessage[] =
+ "%0 must be marked explicit to avoid unintentional implicit conversions";
+
+ if (const auto *Conversion =
+ Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) {
+ if (Conversion->isOutOfLine())
+ return;
+ SourceLocation Loc = Conversion->getLocation();
+ // Ignore all macros until we learn to ignore specific ones (e.g. used in
+ // gmock to define matchers).
+ if (Loc.isMacroID())
+ return;
+ diag(Loc, WarningMessage)
+ << Conversion << FixItHint::CreateInsertion(Loc, "explicit ");
+ return;
+ }
+
+ const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+ if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 ||
+ Ctor->getMinRequiredArguments() > 1)
+ return;
+
+ bool takesInitializerList = isStdInitializerList(
+ Ctor->getParamDecl(0)->getType().getNonReferenceType());
+ if (Ctor->isExplicit() &&
+ (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
+ auto isKWExplicit = [](const Token &Tok) {
+ return Tok.is(tok::raw_identifier) &&
+ Tok.getRawIdentifier() == "explicit";
+ };
+ SourceRange ExplicitTokenRange =
+ FindToken(*Result.SourceManager, getLangOpts(),
+ Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
+ StringRef ConstructorDescription;
+ if (Ctor->isMoveConstructor())
+ ConstructorDescription = "move";
+ else if (Ctor->isCopyConstructor())
+ ConstructorDescription = "copy";
+ else
+ ConstructorDescription = "initializer-list";
+
+ auto Diag = diag(Ctor->getLocation(),
+ "%0 constructor should not be declared explicit")
+ << ConstructorDescription;
+ if (ExplicitTokenRange.isValid()) {
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(ExplicitTokenRange));
+ }
+ return;
+ }
+
+ if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
+ takesInitializerList)
+ return;
+
+ bool SingleArgument =
+ Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();
+ SourceLocation Loc = Ctor->getLocation();
+ diag(Loc, WarningMessage)
+ << (SingleArgument
+ ? "single-argument constructors"
+ : "constructors that are callable with a single argument")
+ << FixItHint::CreateInsertion(Loc, "explicit ");
+}
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ExplicitConstructorCheck.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_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+/// Checks that all single-argument constructors are explicit.
+///
+/// See https://google.github.io/styleguide/cppguide.html#Explicit_Constructors
+class ExplicitConstructorCheck : public ClangTidyCheck {
+public:
+ ExplicitConstructorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
--- /dev/null
+//===--- ExplicitMakePairCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplicitMakePairCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
+ return Node.hasExplicitTemplateArgs();
+}
+} // namespace
+
+namespace tidy {
+namespace google {
+namespace build {
+
+void ExplicitMakePairCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Look for std::make_pair with explicit template args. Ignore calls in
+ // templates.
+ Finder->addMatcher(
+ callExpr(unless(isInTemplateInstantiation()),
+ callee(expr(ignoringParenImpCasts(
+ declRefExpr(hasExplicitTemplateArgs(),
+ to(functionDecl(hasName("::std::make_pair"))))
+ .bind("declref")))))
+ .bind("call"),
+ this);
+}
+
+void ExplicitMakePairCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declref");
+
+ // Sanity check: The use might have overriden ::std::make_pair.
+ if (Call->getNumArgs() != 2)
+ return;
+
+ const Expr *Arg0 = Call->getArg(0)->IgnoreParenImpCasts();
+ const Expr *Arg1 = Call->getArg(1)->IgnoreParenImpCasts();
+
+ // If types don't match, we suggest replacing with std::pair and explicit
+ // template arguments. Otherwise just remove the template arguments from
+ // make_pair.
+ if (Arg0->getType() != Call->getArg(0)->getType() ||
+ Arg1->getType() != Call->getArg(1)->getType()) {
+ diag(Call->getLocStart(), "for C++11-compatibility, use pair directly")
+ << FixItHint::CreateReplacement(
+ SourceRange(DeclRef->getLocStart(), DeclRef->getLAngleLoc()),
+ "std::pair<");
+ } else {
+ diag(Call->getLocStart(),
+ "for C++11-compatibility, omit template arguments from make_pair")
+ << FixItHint::CreateRemoval(
+ SourceRange(DeclRef->getLAngleLoc(), DeclRef->getRAngleLoc()));
+ }
+}
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ExplicitMakePairCheck.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_GOOGLE_EXPLICITMAKEPAIRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITMAKEPAIRCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Check that `make_pair`'s template arguments are deduced.
+///
+/// G++ 4.6 in C++11 mode fails badly if `make_pair`'s template arguments are
+/// specified explicitly, and such use isn't intended in any case.
+///
+/// Corresponding cpplint.py check name: 'build/explicit_make_pair'.
+class ExplicitMakePairCheck : public ClangTidyCheck {
+public:
+ ExplicitMakePairCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITMAKEPAIRCHECK_H
--- /dev/null
+//===--- GlobalNamesInHeadersCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GlobalNamesInHeadersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+GlobalNamesInHeadersCheck::GlobalNamesInHeadersCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ RawStringHeaderFileExtensions(
+ Options.getLocalOrGlobal("HeaderFileExtensions", "h")) {
+ if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions, ',')) {
+ llvm::errs() << "Invalid header file extension: "
+ << RawStringHeaderFileExtensions << "\n";
+ }
+}
+
+void GlobalNamesInHeadersCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void GlobalNamesInHeadersCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(decl(anyOf(usingDecl(), usingDirectiveDecl()),
+ hasDeclContext(translationUnitDecl()))
+ .bind("using_decl"),
+ this);
+}
+
+void GlobalNamesInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *D = Result.Nodes.getNodeAs<Decl>("using_decl");
+ // If it comes from a macro, we'll assume it is fine.
+ if (D->getLocStart().isMacroID())
+ return;
+
+ // Ignore if it comes from the "main" file ...
+ if (Result.SourceManager->isInMainFile(
+ Result.SourceManager->getExpansionLoc(D->getLocStart()))) {
+ // unless that file is a header.
+ if (!utils::isSpellingLocInHeaderFile(
+ D->getLocStart(), *Result.SourceManager, HeaderFileExtensions))
+ return;
+ }
+
+ if (const auto *UsingDirective = dyn_cast<UsingDirectiveDecl>(D)) {
+ if (UsingDirective->getNominatedNamespace()->isAnonymousNamespace()) {
+ // Anynoumous namespaces inject a using directive into the AST to import
+ // the names into the containing namespace.
+ // We should not have them in headers, but there is another warning for
+ // that.
+ return;
+ }
+ }
+
+ diag(D->getLocStart(),
+ "using declarations in the global namespace in headers are prohibited");
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- GlobalNamesInHeadersCheck.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_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Flag global namespace pollution in header files.
+/// Right now it only triggers on using declarations and directives.
+///
+/// The check supports these options:
+/// - `HeaderFileExtensions`: a comma-separated list of filename extensions
+/// of header files (the filename extensions should not contain "." prefix).
+/// "h" by default.
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between "," if there are other filename extensions.
+class GlobalNamesInHeadersCheck : public ClangTidyCheck {
+public:
+ GlobalNamesInHeadersCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const std::string RawStringHeaderFileExtensions;
+ utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
--- /dev/null
+//===--- GoogleTidyModule.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 "../readability/BracesAroundStatementsCheck.h"
+#include "../readability/FunctionSizeCheck.h"
+#include "../readability/NamespaceCommentCheck.h"
+#include "../readability/RedundantSmartptrGetCheck.h"
+#include "AvoidCStyleCastsCheck.h"
+#include "DefaultArgumentsCheck.h"
+#include "ExplicitConstructorCheck.h"
+#include "ExplicitMakePairCheck.h"
+#include "GlobalNamesInHeadersCheck.h"
+#include "IntegerTypesCheck.h"
+#include "NonConstReferences.h"
+#include "OverloadedUnaryAndCheck.h"
+#include "StringReferenceMemberCheck.h"
+#include "TodoCommentCheck.h"
+#include "UnnamedNamespaceInHeaderCheck.h"
+#include "UsingNamespaceDirectiveCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+class GoogleModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<build::ExplicitMakePairCheck>(
+ "google-build-explicit-make-pair");
+ CheckFactories.registerCheck<build::UnnamedNamespaceInHeaderCheck>(
+ "google-build-namespaces");
+ CheckFactories.registerCheck<build::UsingNamespaceDirectiveCheck>(
+ "google-build-using-namespace");
+ CheckFactories.registerCheck<DefaultArgumentsCheck>(
+ "google-default-arguments");
+ CheckFactories.registerCheck<ExplicitConstructorCheck>(
+ "google-explicit-constructor");
+ CheckFactories.registerCheck<runtime::IntegerTypesCheck>(
+ "google-runtime-int");
+ CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
+ "google-runtime-operator");
+ CheckFactories.registerCheck<runtime::NonConstReferences>(
+ "google-runtime-references");
+ CheckFactories.registerCheck<runtime::StringReferenceMemberCheck>(
+ "google-runtime-member-string-references");
+ CheckFactories.registerCheck<readability::AvoidCStyleCastsCheck>(
+ "google-readability-casting");
+ CheckFactories.registerCheck<readability::TodoCommentCheck>(
+ "google-readability-todo");
+ CheckFactories
+ .registerCheck<clang::tidy::readability::BracesAroundStatementsCheck>(
+ "google-readability-braces-around-statements");
+ CheckFactories.registerCheck<readability::GlobalNamesInHeadersCheck>(
+ "google-global-names-in-headers");
+ CheckFactories.registerCheck<clang::tidy::readability::FunctionSizeCheck>(
+ "google-readability-function-size");
+ CheckFactories
+ .registerCheck<clang::tidy::readability::NamespaceCommentCheck>(
+ "google-readability-namespace-comments");
+ CheckFactories
+ .registerCheck<clang::tidy::readability::RedundantSmartptrGetCheck>(
+ "google-readability-redundant-smartptr-get");
+ }
+
+ ClangTidyOptions getModuleOptions() override {
+ ClangTidyOptions Options;
+ auto &Opts = Options.CheckOptions;
+ Opts["google-readability-braces-around-statements.ShortStatementLines"] =
+ "1";
+ Opts["google-readability-function-size.StatementThreshold"] = "800";
+ Opts["google-readability-namespace-comments.ShortNamespaceLines"] = "10";
+ Opts["google-readability-namespace-comments.SpacesBeforeComments"] = "2";
+ return Options;
+ }
+};
+
+// Register the GoogleTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<GoogleModule> X("google-module",
+ "Adds Google lint checks.");
+
+} // namespace google
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the GoogleModule.
+volatile int GoogleModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- IntegerTypesCheck.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 "IntegerTypesCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+
+using namespace ast_matchers;
+
+static Token getTokenAtLoc(SourceLocation Loc,
+ const MatchFinder::MatchResult &MatchResult,
+ IdentifierTable &IdentTable) {
+ Token Tok;
+ if (Lexer::getRawToken(Loc, Tok, *MatchResult.SourceManager,
+ MatchResult.Context->getLangOpts(), false))
+ return Tok;
+
+ if (Tok.is(tok::raw_identifier)) {
+ IdentifierInfo &Info = IdentTable.get(Tok.getRawIdentifier());
+ Tok.setIdentifierInfo(&Info);
+ Tok.setKind(Info.getTokenID());
+ }
+ return Tok;
+}
+
+namespace tidy {
+namespace google {
+namespace runtime {
+
+IntegerTypesCheck::IntegerTypesCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ UnsignedTypePrefix(Options.get("UnsignedTypePrefix", "uint")),
+ SignedTypePrefix(Options.get("SignedTypePrefix", "int")),
+ TypeSuffix(Options.get("TypeSuffix", "")) {}
+
+void IntegerTypesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "UnsignedTypePrefix", UnsignedTypePrefix);
+ Options.store(Opts, "SignedTypePrefix", SignedTypePrefix);
+ Options.store(Opts, "TypeSuffix", TypeSuffix);
+}
+
+void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) {
+ // Find all TypeLocs. The relevant Style Guide rule only applies to C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+ Finder->addMatcher(typeLoc(loc(isInteger())).bind("tl"), this);
+ IdentTable = llvm::make_unique<IdentifierTable>(getLangOpts());
+}
+
+void IntegerTypesCheck::check(const MatchFinder::MatchResult &Result) {
+ auto TL = *Result.Nodes.getNodeAs<TypeLoc>("tl");
+ SourceLocation Loc = TL.getLocStart();
+
+ if (Loc.isInvalid() || Loc.isMacroID())
+ return;
+
+ // Look through qualification.
+ if (auto QualLoc = TL.getAs<QualifiedTypeLoc>())
+ TL = QualLoc.getUnqualifiedLoc();
+
+ auto BuiltinLoc = TL.getAs<BuiltinTypeLoc>();
+ if (!BuiltinLoc)
+ return;
+
+ Token Tok = getTokenAtLoc(Loc, Result, *IdentTable);
+ // Ensure the location actually points to one of the builting integral type
+ // names we're interested in. Otherwise, we might be getting this match from
+ // implicit code (e.g. an implicit assignment operator of a class containing
+ // an array of non-POD types).
+ if (!Tok.isOneOf(tok::kw_short, tok::kw_long, tok::kw_unsigned,
+ tok::kw_signed))
+ return;
+
+ bool IsSigned;
+ unsigned Width;
+ const TargetInfo &TargetInfo = Result.Context->getTargetInfo();
+
+ // Look for uses of short, long, long long and their unsigned versions.
+ switch (BuiltinLoc.getTypePtr()->getKind()) {
+ case BuiltinType::Short:
+ Width = TargetInfo.getShortWidth();
+ IsSigned = true;
+ break;
+ case BuiltinType::Long:
+ Width = TargetInfo.getLongWidth();
+ IsSigned = true;
+ break;
+ case BuiltinType::LongLong:
+ Width = TargetInfo.getLongLongWidth();
+ IsSigned = true;
+ break;
+ case BuiltinType::UShort:
+ Width = TargetInfo.getShortWidth();
+ IsSigned = false;
+ break;
+ case BuiltinType::ULong:
+ Width = TargetInfo.getLongWidth();
+ IsSigned = false;
+ break;
+ case BuiltinType::ULongLong:
+ Width = TargetInfo.getLongLongWidth();
+ IsSigned = false;
+ break;
+ default:
+ return;
+ }
+
+ // We allow "unsigned short port" as that's reasonably common and required by
+ // the sockets API.
+ const StringRef Port = "unsigned short port";
+ const char *Data = Result.SourceManager->getCharacterData(Loc);
+ if (!std::strncmp(Data, Port.data(), Port.size()) &&
+ !isIdentifierBody(Data[Port.size()]))
+ return;
+
+ std::string Replacement =
+ ((IsSigned ? SignedTypePrefix : UnsignedTypePrefix) + Twine(Width) +
+ TypeSuffix)
+ .str();
+
+ // We don't add a fix-it as changing the type can easily break code,
+ // e.g. when a function requires a 'long' argument on all platforms.
+ // QualTypes are printed with implicit quotes.
+ diag(Loc, "consider replacing %0 with '%1'") << BuiltinLoc.getType()
+ << Replacement;
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- IntegerTypesCheck.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_GOOGLE_INTEGERTYPESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
+
+#include "../ClangTidy.h"
+
+#include <memory>
+
+namespace clang {
+
+class IdentifierTable;
+
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds uses of `short`, `long` and `long long` and suggest replacing them
+/// with `u?intXX(_t)?`.
+///
+/// Correspondig cpplint.py check: 'runtime/int'.
+class IntegerTypesCheck : public ClangTidyCheck {
+public:
+ IntegerTypesCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+private:
+ const std::string UnsignedTypePrefix;
+ const std::string SignedTypePrefix;
+ const std::string TypeSuffix;
+
+ std::unique_ptr<IdentifierTable> IdentTable;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
--- /dev/null
+//===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonConstReferences.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+NonConstReferences::NonConstReferences(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WhiteListTypes(
+ utils::options::parseStringList(Options.get("WhiteListTypes", ""))) {}
+
+void NonConstReferences::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WhiteListTypes",
+ utils::options::serializeStringList(WhiteListTypes));
+}
+
+void NonConstReferences::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ parmVarDecl(
+ unless(isInstantiated()),
+ hasType(references(
+ qualType(unless(isConstQualified())).bind("referenced_type"))),
+ unless(hasType(rValueReferenceType())))
+ .bind("param"),
+ this);
+}
+
+void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
+ const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+ const auto *Function =
+ dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
+
+ if (Function == nullptr || Function->isImplicit())
+ return;
+
+ if (!Function->isCanonicalDecl())
+ return;
+
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
+ // Don't warn on implementations of an interface using references.
+ if (Method->begin_overridden_methods() != Method->end_overridden_methods())
+ return;
+ // Don't warn on lambdas, as they frequently have to conform to the
+ // interface defined elsewhere.
+ if (Method->getParent()->isLambda())
+ return;
+ }
+
+ auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
+
+ if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),
+ [&](llvm::StringRef WhiteListType) {
+ return ReferencedType.getCanonicalType().getAsString(
+ Result.Context->getPrintingPolicy()) ==
+ WhiteListType;
+ }) != WhiteListTypes.end())
+ return;
+
+ // Don't warn on function references, they shouldn't be constant.
+ if (ReferencedType->isFunctionProtoType())
+ return;
+
+ // Don't warn on dependent types in templates.
+ if (ReferencedType->isDependentType())
+ return;
+
+ if (Function->isOverloadedOperator()) {
+ switch (Function->getOverloadedOperator()) {
+ case clang::OO_LessLess:
+ case clang::OO_PlusPlus:
+ case clang::OO_MinusMinus:
+ case clang::OO_PlusEqual:
+ case clang::OO_MinusEqual:
+ case clang::OO_StarEqual:
+ case clang::OO_SlashEqual:
+ case clang::OO_PercentEqual:
+ case clang::OO_LessLessEqual:
+ case clang::OO_GreaterGreaterEqual:
+ case clang::OO_PipeEqual:
+ case clang::OO_CaretEqual:
+ case clang::OO_AmpEqual:
+ // Don't warn on the first parameter of operator<<(Stream&, ...),
+ // operator++, operator-- and operation+assignment operators.
+ if (Function->getParamDecl(0) == Parameter)
+ return;
+ break;
+ case clang::OO_GreaterGreater: {
+ auto isNonConstRef = [](clang::QualType T) {
+ return T->isReferenceType() &&
+ !T.getNonReferenceType().isConstQualified();
+ };
+ // Don't warn on parameters of stream extractors:
+ // Stream& operator>>(Stream&, Value&);
+ // Both parameters should be non-const references by convention.
+ if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
+ (Function->getNumParams() < 2 || // E.g. member operator>>.
+ isNonConstRef(Function->getParamDecl(1)->getType())) &&
+ isNonConstRef(Function->getReturnType()))
+ return;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Some functions use references to comply with established standards.
+ if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
+ return;
+
+ // iostream parameters are typically passed by non-const reference.
+ if (StringRef(ReferencedType.getAsString()).endswith("stream"))
+ return;
+
+ if (Parameter->getName().empty()) {
+ diag(Parameter->getLocation(), "non-const reference parameter at index %0, "
+ "make it const or use a pointer")
+ << Parameter->getFunctionScopeIndex();
+ } else {
+ diag(Parameter->getLocation(),
+ "non-const reference parameter %0, make it const or use a pointer")
+ << Parameter;
+ }
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NonConstReferences.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_GOOGLE_NON_CONST_REFERENCES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// \brief Checks the usage of non-constant references in function parameters.
+///
+/// https://google.github.io/styleguide/cppguide.html#Reference_Arguments
+class NonConstReferences : public ClangTidyCheck {
+public:
+ NonConstReferences(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 std::vector<std::string> WhiteListTypes;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
--- /dev/null
+//===--- OverloadedUnaryAndCheck.cpp - clang-tidy ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OverloadedUnaryAndCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+void OverloadedUnaryAndCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Match unary methods that overload operator&.
+ Finder->addMatcher(
+ cxxMethodDecl(parameterCountIs(0), hasOverloadedOperatorName("&"))
+ .bind("overload"),
+ this);
+ // Also match freestanding unary operator& overloads. Be careful not to match
+ // binary methods.
+ Finder->addMatcher(
+ functionDecl(allOf(
+ unless(cxxMethodDecl()),
+ functionDecl(parameterCountIs(1), hasOverloadedOperatorName("&"))
+ .bind("overload"))),
+ this);
+}
+
+void OverloadedUnaryAndCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>("overload");
+ diag(Decl->getLocStart(),
+ "do not overload unary operator&, it is dangerous.");
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- OverloadedUnaryAndCheck.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_GOOGLE_OVERLOADEDUNARYANDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OVERLOADEDUNARYANDCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds overloads of unary `operator &`.
+///
+/// https://google.github.io/styleguide/cppguide.html#Operator_Overloading
+///
+/// Corresponding cpplint.py check name: 'runtime/operator'.
+class OverloadedUnaryAndCheck : public ClangTidyCheck {
+public:
+ OverloadedUnaryAndCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OVERLOADEDUNARYANDCHECK_H
--- /dev/null
+//===--- StringReferenceMemberCheck.cpp - clang-tidy ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringReferenceMemberCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+void StringReferenceMemberCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Look for const references to std::string or ::string.
+ auto String = anyOf(recordDecl(hasName("::std::basic_string")),
+ recordDecl(hasName("::string")));
+ auto ConstString = qualType(isConstQualified(), hasDeclaration(String));
+
+ // Ignore members in template instantiations.
+ Finder->addMatcher(
+ fieldDecl(hasType(references(ConstString)), unless(isInstantiated()))
+ .bind("member"),
+ this);
+}
+
+void StringReferenceMemberCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+ diag(Member->getLocStart(), "const string& members are dangerous; it is much "
+ "better to use alternatives, such as pointers or "
+ "simple constants");
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StringReferenceMemberCheck.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_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds members of type `const string&`.
+///
+/// const string reference members are generally considered unsafe as they can
+/// be created from a temporary quite easily.
+///
+/// \code
+/// struct S {
+/// S(const string &Str) : Str(Str) {}
+/// const string &Str;
+/// };
+/// S instance("string");
+/// \endcode
+///
+/// In the constructor call a string temporary is created from `const char *`
+/// and destroyed immediately after the call. This leaves around a dangling
+/// reference.
+///
+/// This check emit warnings for both `std::string` and `::string` const
+/// reference members.
+///
+/// Corresponding cpplint.py check name: 'runtime/member_string_reference'.
+class StringReferenceMemberCheck : public ClangTidyCheck {
+public:
+ StringReferenceMemberCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
--- /dev/null
+//===--- TodoCommentCheck.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 "TodoCommentCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+class TodoCommentCheck::TodoCommentHandler : public CommentHandler {
+public:
+ TodoCommentHandler(TodoCommentCheck &Check, llvm::Optional<std::string> User)
+ : Check(Check), User(User ? *User : "unknown"),
+ TodoMatch("^// *TODO *(\\(.*\\))?:?( )?(.*)$") {}
+
+ bool HandleComment(Preprocessor &PP, SourceRange Range) override {
+ StringRef Text =
+ Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+ PP.getSourceManager(), PP.getLangOpts());
+
+ SmallVector<StringRef, 4> Matches;
+ if (!TodoMatch.match(Text, &Matches))
+ return false;
+
+ StringRef Username = Matches[1];
+ StringRef Comment = Matches[3];
+
+ if (!Username.empty())
+ return false;
+
+ std::string NewText = ("// TODO(" + Twine(User) + "): " + Comment).str();
+
+ Check.diag(Range.getBegin(), "missing username/bug in TODO")
+ << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Range),
+ NewText);
+ return false;
+ }
+
+private:
+ TodoCommentCheck &Check;
+ std::string User;
+ llvm::Regex TodoMatch;
+};
+
+TodoCommentCheck::TodoCommentCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ Handler(llvm::make_unique<TodoCommentHandler>(
+ *this, Context->getOptions().User)) {}
+
+void TodoCommentCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addCommentHandler(Handler.get());
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TodoCommentCheck.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_GOOGLE_TODOCOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Finds TODO comments without a username or bug number.
+///
+/// Corresponding cpplint.py check: 'readability/todo'
+class TodoCommentCheck : public ClangTidyCheck {
+public:
+ TodoCommentCheck(StringRef Name, ClangTidyContext *Context);
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+private:
+ class TodoCommentHandler;
+ std::unique_ptr<TodoCommentHandler> Handler;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
--- /dev/null
+//===--- UnnamedNamespaceInHeaderCheck.cpp - clang-tidy ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnnamedNamespaceInHeaderCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+UnnamedNamespaceInHeaderCheck::UnnamedNamespaceInHeaderCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ RawStringHeaderFileExtensions(
+ Options.getLocalOrGlobal("HeaderFileExtensions", "h,hh,hpp,hxx")) {
+ if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions, ',')) {
+ llvm::errs() << "Invalid header file extension: "
+ << RawStringHeaderFileExtensions << "\n";
+ }
+}
+
+void UnnamedNamespaceInHeaderCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void UnnamedNamespaceInHeaderCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (getLangOpts().CPlusPlus)
+ Finder->addMatcher(namespaceDecl(isAnonymous()).bind("anonymousNamespace"),
+ this);
+}
+
+void UnnamedNamespaceInHeaderCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("anonymousNamespace");
+ SourceLocation Loc = N->getLocStart();
+ if (!Loc.isValid())
+ return;
+
+ if (utils::isPresumedLocInHeaderFile(Loc, *Result.SourceManager,
+ HeaderFileExtensions))
+ diag(Loc, "do not use unnamed namespaces in header files");
+}
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnnamedNamespaceInHeaderCheck.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_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Finds anonymous namespaces in headers.
+///
+/// The check supports these options:
+/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+/// header files (The filename extensions should not contain "." prefix).
+/// "h,hh,hpp,hxx" by default.
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between "," if there are other filename extensions.
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+///
+/// Corresponding cpplint.py check name: 'build/namespaces'.
+class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
+public:
+ UnnamedNamespaceInHeaderCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const std::string RawStringHeaderFileExtensions;
+ utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
--- /dev/null
+//===--- UsingNamespaceDirectiveCheck.cpp - clang-tidy ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UsingNamespaceDirectiveCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+void UsingNamespaceDirectiveCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (getLangOpts().CPlusPlus)
+ Finder->addMatcher(usingDirectiveDecl().bind("usingNamespace"), this);
+}
+
+void UsingNamespaceDirectiveCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *U = Result.Nodes.getNodeAs<UsingDirectiveDecl>("usingNamespace");
+ SourceLocation Loc = U->getLocStart();
+ if (U->isImplicit() || !Loc.isValid())
+ return;
+
+ // Do not warn if namespace is a std namespace with user-defined literals. The
+ // user-defined literals can only be used with a using directive.
+ if (isStdLiteralsNamespace(U->getNominatedNamespace()))
+ return;
+
+ diag(Loc, "do not use namespace using-directives; "
+ "use using-declarations instead");
+ // TODO: We could suggest a list of using directives replacing the using
+ // namespace directive.
+}
+
+bool UsingNamespaceDirectiveCheck::isStdLiteralsNamespace(
+ const NamespaceDecl *NS) {
+ if (!NS->getName().endswith("literals"))
+ return false;
+
+ const auto *Parent = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
+ if (!Parent)
+ return false;
+
+ if (Parent->isStdNamespace())
+ return true;
+
+ return Parent->getName() == "literals" && Parent->getParent() &&
+ Parent->getParent()->isStdNamespace();
+}
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UsingNamespaceDirectiveCheck.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_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Finds using namespace directives.
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+///
+/// The check implements the following rule of the Google C++ Style Guide:
+///
+/// You may not use a using-directive to make all names from a namespace
+/// available.
+///
+/// \code
+/// // Forbidden -- This pollutes the namespace.
+/// using namespace foo;
+/// \endcode
+///
+/// Corresponding cpplint.py check name: `build/namespaces`.
+class UsingNamespaceDirectiveCheck : public ClangTidyCheck {
+public:
+ UsingNamespaceDirectiveCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ static bool isStdLiteralsNamespace(const NamespaceDecl *NS);
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyHICPPModule
+ NoAssemblerCheck.cpp
+ HICPPTidyModule.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyCppCoreGuidelinesModule
+ clangTidyGoogleModule
+ clangTidyMiscModule
+ clangTidyModernizeModule
+ clangTidyReadabilityModule
+ clangTidyUtils
+ )
--- /dev/null
+//===------- HICPPTidyModule.cpp - clang-tidy -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../cppcoreguidelines/ProTypeMemberInitCheck.h"
+#include "../cppcoreguidelines/SpecialMemberFunctionsCheck.h"
+#include "../google/DefaultArgumentsCheck.h"
+#include "../google/ExplicitConstructorCheck.h"
+#include "../misc/NewDeleteOverloadsCheck.h"
+#include "../misc/NoexceptMoveConstructorCheck.h"
+#include "../misc/UndelegatedConstructor.h"
+#include "../misc/UseAfterMoveCheck.h"
+#include "../modernize/UseEqualsDefaultCheck.h"
+#include "../modernize/UseEqualsDeleteCheck.h"
+#include "../modernize/UseOverrideCheck.h"
+#include "../readability/FunctionSizeCheck.h"
+#include "../readability/IdentifierNamingCheck.h"
+#include "NoAssemblerCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+class HICPPModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<google::ExplicitConstructorCheck>(
+ "hicpp-explicit-conversions");
+ CheckFactories.registerCheck<readability::FunctionSizeCheck>(
+ "hicpp-function-size");
+ CheckFactories.registerCheck<readability::IdentifierNamingCheck>(
+ "hicpp-named-parameter");
+ CheckFactories.registerCheck<misc::UseAfterMoveCheck>(
+ "hicpp-invalid-access-moved");
+ CheckFactories.registerCheck<cppcoreguidelines::ProTypeMemberInitCheck>(
+ "hicpp-member-init");
+ CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
+ "hicpp-new-delete-operators");
+ CheckFactories.registerCheck<misc::NoexceptMoveConstructorCheck>(
+ "hicpp-noexcept-move");
+ CheckFactories.registerCheck<NoAssemblerCheck>("hicpp-no-assembler");
+ CheckFactories
+ .registerCheck<cppcoreguidelines::SpecialMemberFunctionsCheck>(
+ "hicpp-special-member-functions");
+ CheckFactories.registerCheck<misc::UndelegatedConstructorCheck>(
+ "hicpp-undelegated-constructor");
+ CheckFactories.registerCheck<modernize::UseEqualsDefaultCheck>(
+ "hicpp-use-equals-default");
+ CheckFactories.registerCheck<modernize::UseEqualsDeleteCheck>(
+ "hicpp-use-equals-delete");
+ CheckFactories.registerCheck<modernize::UseOverrideCheck>(
+ "hicpp-use-override");
+ }
+};
+
+// Register the HICPPModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<HICPPModule>
+ X("hicpp-module", "Adds High-Integrity C++ checks.");
+
+} // namespace hicpp
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the HICPPModule.
+volatile int HICPPModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+------------------------------------------------------------------------------
+clang-tidy High-Integrity C++ Files
+------------------------------------------------------------------------------
+All clang-tidy files are licensed under the LLVM license with the following
+additions:
+
+Any file referencing a High-Integrity C++ Coding guideline:
+
+HIC++ Coding Standard as created by PRQA.
+
+Please see http://www.codingstandard.com/section/conditions-of-use/ for more
+information.
--- /dev/null
+//===--- NoAssemblerCheck.cpp - clang-tidy---------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NoAssemblerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER(VarDecl, isAsm) { return Node.hasAttr<clang::AsmLabelAttr>(); }
+const internal::VariadicDynCastAllOfMatcher<Decl, FileScopeAsmDecl>
+ fileScopeAsmDecl;
+}
+}
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+void NoAssemblerCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(asmStmt().bind("asm-stmt"), this);
+ Finder->addMatcher(fileScopeAsmDecl().bind("asm-file-scope"), this);
+ Finder->addMatcher(varDecl(isAsm()).bind("asm-var"), this);
+}
+
+void NoAssemblerCheck::check(const MatchFinder::MatchResult &Result) {
+ SourceLocation ASMLocation;
+ if (const auto *ASM = Result.Nodes.getNodeAs<AsmStmt>("asm-stmt"))
+ ASMLocation = ASM->getAsmLoc();
+ else if (const auto *ASM =
+ Result.Nodes.getNodeAs<FileScopeAsmDecl>("asm-file-scope"))
+ ASMLocation = ASM->getAsmLoc();
+ else if (const auto *ASM = Result.Nodes.getNodeAs<VarDecl>("asm-var"))
+ ASMLocation = ASM->getLocation();
+ else
+ llvm_unreachable("Unhandled case in matcher.");
+
+ diag(ASMLocation, "do not use inline assembler in safety-critical code");
+}
+
+} // namespace hicpp
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NoAssemblerCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+/// Find assembler statements. No fix is offered.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp-no-assembler.html
+class NoAssemblerCheck : public ClangTidyCheck {
+public:
+ NoAssemblerCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace hicpp
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyLLVMModule
+ HeaderGuardCheck.cpp
+ IncludeOrderCheck.cpp
+ LLVMTidyModule.cpp
+ TwineLocalCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyReadabilityModule
+ clangTidyUtils
+ clangTooling
+ )
--- /dev/null
+//===--- HeaderGuardCheck.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 "HeaderGuardCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+LLVMHeaderGuardCheck::LLVMHeaderGuardCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : HeaderGuardCheck(Name, Context),
+ RawStringHeaderFileExtensions(
+ Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+ utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions, ',');
+}
+
+void LLVMHeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+bool LLVMHeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) {
+ return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename,
+ StringRef OldGuard) {
+ std::string Guard = tooling::getAbsolutePath(Filename);
+
+ // Sanitize the path. There are some rules for compatibility with the historic
+ // style in include/llvm and include/clang which we want to preserve.
+
+ // We don't want _INCLUDE_ in our guards.
+ size_t PosInclude = Guard.rfind("include/");
+ if (PosInclude != StringRef::npos)
+ Guard = Guard.substr(PosInclude + std::strlen("include/"));
+
+ // For clang we drop the _TOOLS_.
+ size_t PosToolsClang = Guard.rfind("tools/clang/");
+ if (PosToolsClang != StringRef::npos)
+ Guard = Guard.substr(PosToolsClang + std::strlen("tools/"));
+
+ // The remainder is LLVM_FULL_PATH_TO_HEADER_H
+ size_t PosLLVM = Guard.rfind("llvm/");
+ if (PosLLVM != StringRef::npos)
+ Guard = Guard.substr(PosLLVM);
+
+ std::replace(Guard.begin(), Guard.end(), '/', '_');
+ std::replace(Guard.begin(), Guard.end(), '.', '_');
+ std::replace(Guard.begin(), Guard.end(), '-', '_');
+
+ // The prevalent style in clang is LLVM_CLANG_FOO_BAR_H
+ if (StringRef(Guard).startswith("clang"))
+ Guard = "LLVM_" + Guard;
+
+ return StringRef(Guard).upper();
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- HeaderGuardCheck.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_LLVM_HEADER_GUARD_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
+
+#include "../utils/HeaderGuard.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Finds and fixes header guards that do not adhere to LLVM style.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/llvm-header-guard.html
+/// The check supports these options:
+/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+/// header files (The filename extension should not contain "." prefix).
+/// ",h,hh,hpp,hxx" by default.
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between "," if there are other filename extensions.
+class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
+public:
+ LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
+ bool shouldFixHeaderGuard(StringRef Filename) override;
+ std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+
+private:
+ std::string RawStringHeaderFileExtensions;
+ utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
--- /dev/null
+//===--- IncludeOrderCheck.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 "IncludeOrderCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+namespace {
+class IncludeOrderPPCallbacks : public PPCallbacks {
+public:
+ explicit IncludeOrderPPCallbacks(ClangTidyCheck &Check, SourceManager &SM)
+ : LookForMainModule(true), Check(Check), SM(SM) {}
+
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath,
+ const Module *Imported) override;
+ void EndOfMainFile() override;
+
+private:
+ struct IncludeDirective {
+ SourceLocation Loc; ///< '#' location in the include directive
+ CharSourceRange Range; ///< SourceRange for the file name
+ std::string Filename; ///< Filename as a string
+ bool IsAngled; ///< true if this was an include with angle brackets
+ bool IsMainModule; ///< true if this was the first include in a file
+ };
+
+ typedef std::vector<IncludeDirective> FileIncludes;
+ std::map<clang::FileID, FileIncludes> IncludeDirectives;
+ bool LookForMainModule;
+
+ ClangTidyCheck &Check;
+ SourceManager &SM;
+};
+} // namespace
+
+void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ ::llvm::make_unique<IncludeOrderPPCallbacks>(
+ *this, Compiler.getSourceManager()));
+}
+
+static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule) {
+ // We leave the main module header at the top.
+ if (IsMainModule)
+ return 0;
+
+ // LLVM and clang headers are in the penultimate position.
+ if (Filename.startswith("llvm/") || Filename.startswith("llvm-c/") ||
+ Filename.startswith("clang/") || Filename.startswith("clang-c/"))
+ return 2;
+
+ // System headers are sorted to the end.
+ if (IsAngled || Filename.startswith("gtest/"))
+ return 3;
+
+ // Other headers are inserted between the main module header and LLVM headers.
+ return 1;
+}
+
+void IncludeOrderPPCallbacks::InclusionDirective(
+ SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+ bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
+ // We recognize the first include as a special main module header and want
+ // to leave it in the top position.
+ IncludeDirective ID = {HashLoc, FilenameRange, FileName, IsAngled, false};
+ if (LookForMainModule && !IsAngled) {
+ ID.IsMainModule = true;
+ LookForMainModule = false;
+ }
+
+ // Bucket the include directives by the id of the file they were declared in.
+ IncludeDirectives[SM.getFileID(HashLoc)].push_back(std::move(ID));
+}
+
+void IncludeOrderPPCallbacks::EndOfMainFile() {
+ LookForMainModule = true;
+ if (IncludeDirectives.empty())
+ return;
+
+ // TODO: find duplicated includes.
+
+ // Form blocks of includes. We don't want to sort across blocks. This also
+ // implicitly makes us never reorder over #defines or #if directives.
+ // FIXME: We should be more careful about sorting below comments as we don't
+ // know if the comment refers to the next include or the whole block that
+ // follows.
+ for (auto &Bucket : IncludeDirectives) {
+ auto &FileDirectives = Bucket.second;
+ std::vector<unsigned> Blocks(1, 0);
+ for (unsigned I = 1, E = FileDirectives.size(); I != E; ++I)
+ if (SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
+ SM.getExpansionLineNumber(FileDirectives[I - 1].Loc) + 1)
+ Blocks.push_back(I);
+ Blocks.push_back(FileDirectives.size()); // Sentinel value.
+
+ // Get a vector of indices.
+ std::vector<unsigned> IncludeIndices;
+ for (unsigned I = 0, E = FileDirectives.size(); I != E; ++I)
+ IncludeIndices.push_back(I);
+
+ // Sort the includes. We first sort by priority, then lexicographically.
+ for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
+ std::sort(IncludeIndices.begin() + Blocks[BI],
+ IncludeIndices.begin() + Blocks[BI + 1],
+ [&FileDirectives](unsigned LHSI, unsigned RHSI) {
+ IncludeDirective &LHS = FileDirectives[LHSI];
+ IncludeDirective &RHS = FileDirectives[RHSI];
+
+ int PriorityLHS =
+ getPriority(LHS.Filename, LHS.IsAngled, LHS.IsMainModule);
+ int PriorityRHS =
+ getPriority(RHS.Filename, RHS.IsAngled, RHS.IsMainModule);
+
+ return std::tie(PriorityLHS, LHS.Filename) <
+ std::tie(PriorityRHS, RHS.Filename);
+ });
+
+ // Emit a warning for each block and fixits for all changes within that
+ // block.
+ for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
+ // Find the first include that's not in the right position.
+ unsigned I, E;
+ for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I)
+ if (IncludeIndices[I] != I)
+ break;
+
+ if (I == E)
+ continue;
+
+ // Emit a warning.
+ auto D = Check.diag(FileDirectives[I].Loc,
+ "#includes are not sorted properly");
+
+ // Emit fix-its for all following includes in this block.
+ for (; I != E; ++I) {
+ if (IncludeIndices[I] == I)
+ continue;
+ const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
+
+ SourceLocation FromLoc = CopyFrom.Range.getBegin();
+ const char *FromData = SM.getCharacterData(FromLoc);
+ unsigned FromLen = std::strcspn(FromData, "\n");
+
+ StringRef FixedName(FromData, FromLen);
+
+ SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
+ const char *ToData = SM.getCharacterData(ToLoc);
+ unsigned ToLen = std::strcspn(ToData, "\n");
+ auto ToRange =
+ CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
+
+ D << FixItHint::CreateReplacement(ToRange, FixedName);
+ }
+ }
+ }
+
+ IncludeDirectives.clear();
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- IncludeOrderCheck.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_LLVM_INCLUDE_ORDER_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INCLUDE_ORDER_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Checks the correct order of `#includes`.
+///
+/// See http://llvm.org/docs/CodingStandards.html#include-style
+class IncludeOrderCheck : public ClangTidyCheck {
+public:
+ IncludeOrderCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INCLUDE_ORDER_CHECK_H
--- /dev/null
+//===--- LLVMTidyModule.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 "../readability/NamespaceCommentCheck.h"
+#include "HeaderGuardCheck.h"
+#include "IncludeOrderCheck.h"
+#include "TwineLocalCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+class LLVMModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<LLVMHeaderGuardCheck>("llvm-header-guard");
+ CheckFactories.registerCheck<IncludeOrderCheck>("llvm-include-order");
+ CheckFactories.registerCheck<readability::NamespaceCommentCheck>(
+ "llvm-namespace-comment");
+ CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
+ }
+};
+
+// Register the LLVMTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<LLVMModule> X("llvm-module",
+ "Adds LLVM lint checks.");
+
+} // namespace llvm
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the LLVMModule.
+volatile int LLVMModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TwineLocalCheck.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 "TwineLocalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+void TwineLocalCheck::registerMatchers(MatchFinder *Finder) {
+ auto TwineType =
+ qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine"))));
+ Finder->addMatcher(varDecl(hasType(TwineType)).bind("variable"), this);
+}
+
+void TwineLocalCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *VD = Result.Nodes.getNodeAs<VarDecl>("variable");
+ auto Diag = diag(VD->getLocation(),
+ "twine variables are prone to use-after-free bugs");
+
+ // If this VarDecl has an initializer try to fix it.
+ if (VD->hasInit()) {
+ // Peel away implicit constructors and casts so we can see the actual type
+ // of the initializer.
+ const Expr *C = VD->getInit()->IgnoreImplicit();
+
+ while (isa<CXXConstructExpr>(C)) {
+ if (cast<CXXConstructExpr>(C)->getNumArgs() == 0)
+ break;
+ C = cast<CXXConstructExpr>(C)->getArg(0)->IgnoreParenImpCasts();
+ }
+
+ SourceRange TypeRange =
+ VD->getTypeSourceInfo()->getTypeLoc().getSourceRange();
+
+ // A real Twine, turn it into a std::string.
+ if (VD->getType()->getCanonicalTypeUnqualified() ==
+ C->getType()->getCanonicalTypeUnqualified()) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ VD->getInit()->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+ Diag << FixItHint::CreateReplacement(TypeRange, "std::string")
+ << FixItHint::CreateInsertion(VD->getInit()->getLocStart(), "(")
+ << FixItHint::CreateInsertion(EndLoc, ").str()");
+ } else {
+ // Just an implicit conversion. Insert the real type.
+ Diag << FixItHint::CreateReplacement(
+ TypeRange,
+ C->getType().getAsString(Result.Context->getPrintingPolicy()));
+ }
+ }
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TwineLocalCheck.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_LLVM_TWINE_LOCAL_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TWINE_LOCAL_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Looks for local `Twine` variables which are prone to use after frees and
+/// should be generally avoided.
+class TwineLocalCheck : public ClangTidyCheck {
+public:
+ TwineLocalCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TWINE_LOCAL_CHECK_H
--- /dev/null
+//===--- ArgumentCommentCheck.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 "ArgumentCommentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Token.h"
+#include "../utils/LexerUtils.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+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(
+ hasAnyName("NewCallback", "NewPermanentCallback")))))
+ .bind("expr"),
+ this);
+ Finder->addMatcher(cxxConstructExpr().bind("expr"), this);
+}
+
+static std::vector<std::pair<SourceLocation, StringRef>>
+getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) {
+ std::vector<std::pair<SourceLocation, StringRef>> Comments;
+ auto &SM = Ctx->getSourceManager();
+ std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
+ EndLoc = SM.getDecomposedLoc(Range.getEnd());
+
+ if (BeginLoc.first != EndLoc.first)
+ return Comments;
+
+ bool Invalid = false;
+ StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
+ if (Invalid)
+ return Comments;
+
+ const char *StrData = Buffer.data() + BeginLoc.second;
+
+ Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
+ Buffer.begin(), StrData, Buffer.end());
+ TheLexer.SetCommentRetentionState(true);
+
+ while (true) {
+ Token Tok;
+ if (TheLexer.LexFromRawLexer(Tok))
+ break;
+ if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
+ break;
+
+ if (Tok.is(tok::comment)) {
+ std::pair<FileID, unsigned> CommentLoc =
+ SM.getDecomposedLoc(Tok.getLocation());
+ assert(CommentLoc.first == BeginLoc.first);
+ Comments.emplace_back(
+ Tok.getLocation(),
+ StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
+ } else {
+ // Clear comments found before the different token, e.g. comma.
+ Comments.clear();
+ }
+ }
+
+ return Comments;
+}
+
+static std::vector<std::pair<SourceLocation, StringRef>>
+getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
+ std::vector<std::pair<SourceLocation, StringRef>> Comments;
+ while (Loc.isValid()) {
+ clang::Token Tok =
+ utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false);
+ if (Tok.isNot(tok::comment))
+ break;
+ Loc = Tok.getLocation();
+ Comments.emplace_back(
+ Loc,
+ Lexer::getSourceText(CharSourceRange::getCharRange(
+ Loc, Loc.getLocWithOffset(Tok.getLength())),
+ Ctx->getSourceManager(), Ctx->getLangOpts()));
+ }
+ return Comments;
+}
+
+static bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> 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 = ArgNameLower.edit_distance(
+ Params[ArgIndex]->getIdentifier()->getName().lower(),
+ /*AllowReplacements=*/true, UpperBound);
+ if (ThisED >= UpperBound)
+ return false;
+
+ for (unsigned I = 0, E = Params.size(); I != E; ++I) {
+ if (I == ArgIndex)
+ continue;
+ IdentifierInfo *II = Params[I]->getIdentifier();
+ if (!II)
+ continue;
+
+ const unsigned Threshold = 2;
+ // 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 = ArgNameLower.edit_distance(II->getName().lower(),
+ /*AllowReplacements=*/true,
+ ThisED + Threshold);
+ if (OtherED < ThisED + Threshold)
+ return false;
+ }
+
+ 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;
+}
+
+static bool looksLikeExpectMethod(const CXXMethodDecl *Expect) {
+ return Expect != nullptr && Expect->getLocation().isMacroID() &&
+ Expect->getNameInfo().getName().isIdentifier() &&
+ Expect->getName().startswith("gmock_");
+}
+static bool areMockAndExpectMethods(const CXXMethodDecl *Mock,
+ const CXXMethodDecl *Expect) {
+ assert(looksLikeExpectMethod(Expect));
+ return Mock != nullptr && Mock->getNextDeclInContext() == Expect &&
+ Mock->getNumParams() == Expect->getNumParams() &&
+ Mock->getLocation().isMacroID() &&
+ Mock->getNameInfo().getName().isIdentifier() &&
+ Mock->getName() == Expect->getName().substr(strlen("gmock_"));
+}
+
+// This uses implementation details of MOCK_METHODx_ macros: for each mocked
+// method M it defines M() with appropriate signature and a method used to set
+// up expectations - gmock_M() - with each argument's type changed the
+// corresponding matcher. This function returns M when given either M or
+// gmock_M.
+static const CXXMethodDecl *findMockedMethod(const CXXMethodDecl *Method) {
+ if (looksLikeExpectMethod(Method)) {
+ const DeclContext *Ctx = Method->getDeclContext();
+ if (Ctx == nullptr || !Ctx->isRecord())
+ return nullptr;
+ for (const auto *D : Ctx->decls()) {
+ if (D->getNextDeclInContext() == Method) {
+ const auto *Previous = dyn_cast<CXXMethodDecl>(D);
+ return areMockAndExpectMethods(Previous, Method) ? Previous : nullptr;
+ }
+ }
+ return nullptr;
+ }
+ if (const auto *Next = dyn_cast_or_null<CXXMethodDecl>(
+ Method->getNextDeclInContext())) {
+ if (looksLikeExpectMethod(Next) && areMockAndExpectMethods(Method, Next))
+ return Method;
+ }
+ return nullptr;
+}
+
+// For gmock expectation builder method (the target of the call generated by
+// `EXPECT_CALL(obj, Method(...))`) tries to find the real method being mocked
+// (returns nullptr, if the mock method doesn't override anything). For other
+// functions returns the function itself.
+static const FunctionDecl *resolveMocks(const FunctionDecl *Func) {
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
+ if (const auto *MockedMethod = findMockedMethod(Method)) {
+ // If mocked method overrides the real one, we can use its parameter
+ // names, otherwise we're out of luck.
+ if (MockedMethod->size_overridden_methods() > 0) {
+ return *MockedMethod->begin_overridden_methods();
+ }
+ return nullptr;
+ }
+ }
+ return Func;
+}
+
+void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
+ const FunctionDecl *OriginalCallee,
+ SourceLocation ArgBeginLoc,
+ llvm::ArrayRef<const Expr *> Args) {
+ const FunctionDecl *Callee = resolveMocks(OriginalCallee);
+ if (!Callee)
+ return;
+
+ Callee = Callee->getFirstDecl();
+ unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
+ if (NumArgs == 0)
+ return;
+
+ auto makeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
+ return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
+ Ctx->getSourceManager(),
+ Ctx->getLangOpts());
+ };
+
+ for (unsigned I = 0; I < NumArgs; ++I) {
+ const ParmVarDecl *PVD = Callee->getParamDecl(I);
+ IdentifierInfo *II = PVD->getIdentifier();
+ if (!II)
+ continue;
+ if (auto Template = Callee->getTemplateInstantiationPattern()) {
+ // 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()) {
+ continue;
+ }
+ }
+
+ CharSourceRange BeforeArgument =
+ makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart());
+ ArgBeginLoc = Args[I]->getLocEnd();
+
+ std::vector<std::pair<SourceLocation, StringRef>> Comments;
+ if (BeforeArgument.isValid()) {
+ Comments = getCommentsInRange(Ctx, BeforeArgument);
+ } else {
+ // Fall back to parsing back from the start of the argument.
+ CharSourceRange ArgsRange = makeFileCharRange(
+ Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd());
+ Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin());
+ }
+
+ for (auto Comment : Comments) {
+ llvm::SmallVector<StringRef, 2> Matches;
+ if (IdentRE.match(Comment.second, &Matches) &&
+ !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)) {
+ Diag << FixItHint::CreateReplacement(
+ Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
+ }
+ }
+ diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II;
+ if (OriginalCallee != Callee) {
+ diag(OriginalCallee->getLocation(),
+ "actual callee (%0) is declared here", DiagnosticIDs::Note)
+ << OriginalCallee;
+ }
+ }
+ }
+ }
+}
+
+void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+ if (const auto *Call = dyn_cast<CallExpr>(E)) {
+ const FunctionDecl *Callee = Call->getDirectCallee();
+ if (!Callee)
+ return;
+
+ checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
+ llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
+ } else {
+ const auto *Construct = cast<CXXConstructExpr>(E);
+ if (Construct->getNumArgs() == 1 &&
+ Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
+ // Ignore implicit construction.
+ return;
+ }
+ checkCallArgs(
+ Result.Context, Construct->getConstructor(),
+ Construct->getParenOrBraceRange().getBegin(),
+ llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ArgumentCommentCheck.h - clang-tidy --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks that argument comments match parameter names.
+///
+/// The check understands argument comments in the form `/*parameter_name=*/`
+/// that are placed right before the argument.
+///
+/// \code
+/// void f(bool foo);
+///
+/// ...
+/// f(/*bar=*/true);
+/// // warning: argument name 'bar' in comment does not match parameter name 'foo'
+/// \endcode
+///
+/// The check tries to detect typos and suggest automated fixes for them.
+class ArgumentCommentCheck : public ClangTidyCheck {
+public:
+ ArgumentCommentCheck(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;
+ llvm::Regex IdentRE;
+
+ void checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee,
+ SourceLocation ArgBeginLoc,
+ llvm::ArrayRef<const Expr *> Args);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
--- /dev/null
+//===--- AssertSideEffectCheck.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 "AssertSideEffectCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) {
+ const Expr *E = &Node;
+
+ if (const auto *Op = dyn_cast<UnaryOperator>(E)) {
+ UnaryOperator::Opcode OC = Op->getOpcode();
+ return OC == UO_PostInc || OC == UO_PostDec || OC == UO_PreInc ||
+ OC == UO_PreDec;
+ }
+
+ if (const auto *Op = dyn_cast<BinaryOperator>(E)) {
+ return Op->isAssignmentOp();
+ }
+
+ if (const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+ OverloadedOperatorKind OpKind = OpCallExpr->getOperator();
+ return OpKind == OO_Equal || OpKind == OO_PlusEqual ||
+ OpKind == OO_MinusEqual || OpKind == OO_StarEqual ||
+ OpKind == OO_SlashEqual || OpKind == OO_AmpEqual ||
+ OpKind == OO_PipeEqual || OpKind == OO_CaretEqual ||
+ OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual ||
+ OpKind == OO_PlusPlus || OpKind == OO_MinusMinus ||
+ OpKind == OO_PercentEqual || OpKind == OO_New ||
+ OpKind == OO_Delete || OpKind == OO_Array_New ||
+ OpKind == OO_Array_Delete;
+ }
+
+ if (const auto *CExpr = dyn_cast<CallExpr>(E)) {
+ bool Result = CheckFunctionCalls;
+ if (const auto *FuncDecl = CExpr->getDirectCallee()) {
+ if (FuncDecl->getDeclName().isIdentifier() &&
+ FuncDecl->getName() == "__builtin_expect") // exceptions come here
+ Result = false;
+ else if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
+ Result &= !MethodDecl->isConst();
+ }
+ return Result;
+ }
+
+ return isa<CXXNewExpr>(E) || isa<CXXDeleteExpr>(E) || isa<CXXThrowExpr>(E);
+}
+
+} // namespace
+
+AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ CheckFunctionCalls(Options.get("CheckFunctionCalls", false)),
+ RawAssertList(Options.get("AssertMacros", "assert")) {
+ StringRef(RawAssertList).split(AssertMacros, ",", -1, false);
+}
+
+// The options are explained in AssertSideEffectCheck.h.
+void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "CheckFunctionCalls", CheckFunctionCalls);
+ Options.store(Opts, "AssertMacros", RawAssertList);
+}
+
+void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) {
+ auto DescendantWithSideEffect =
+ hasDescendant(expr(hasSideEffect(CheckFunctionCalls)));
+ auto ConditionWithSideEffect = hasCondition(DescendantWithSideEffect);
+ Finder->addMatcher(
+ stmt(
+ anyOf(conditionalOperator(ConditionWithSideEffect),
+ ifStmt(ConditionWithSideEffect),
+ unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(unaryOperator(
+ hasOperatorName("!"),
+ hasUnaryOperand(DescendantWithSideEffect))))))
+ .bind("condStmt"),
+ this);
+}
+
+void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const LangOptions LangOpts = getLangOpts();
+ SourceLocation Loc = Result.Nodes.getNodeAs<Stmt>("condStmt")->getLocStart();
+
+ StringRef AssertMacroName;
+ while (Loc.isValid() && Loc.isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LangOpts);
+
+ // Check if this macro is an assert.
+ if (std::find(AssertMacros.begin(), AssertMacros.end(), MacroName) !=
+ AssertMacros.end()) {
+ AssertMacroName = MacroName;
+ break;
+ }
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+ if (AssertMacroName.empty())
+ return;
+
+ diag(Loc, "found %0() with side effect") << AssertMacroName;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- AssertSideEffectCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds `assert()` with side effect.
+///
+/// The condition of `assert()` is evaluated only in debug builds so a
+/// condition with side effect can cause different behavior in debug / release
+/// builds.
+///
+/// There are two options:
+///
+/// - `AssertMacros`: A comma-separated list of the names of assert macros to
+/// be checked.
+/// - `CheckFunctionCalls`: Whether to treat non-const member and non-member
+/// functions as they produce side effects. Disabled by default because it
+/// can increase the number of false positive warnings.
+class AssertSideEffectCheck : public ClangTidyCheck {
+public:
+ AssertSideEffectCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool CheckFunctionCalls;
+ const std::string RawAssertList;
+ SmallVector<StringRef, 5> AssertMacros;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
--- /dev/null
+//===--- BoolPointerImplicitConversionCheck.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 "BoolPointerImplicitConversionCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
+ // Look for ifs that have an implicit bool* to bool conversion in the
+ // condition. Filter negations.
+ Finder->addMatcher(
+ ifStmt(hasCondition(findAll(implicitCastExpr(
+ allOf(unless(hasParent(unaryOperator(hasOperatorName("!")))),
+ hasSourceExpression(expr(
+ hasType(pointerType(pointee(booleanType()))),
+ ignoringParenImpCasts(declRefExpr().bind("expr")))),
+ hasCastKind(CK_PointerToBoolean))))),
+ unless(isInTemplateInstantiation()))
+ .bind("if"),
+ this);
+}
+
+void BoolPointerImplicitConversionCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+ auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
+
+ // Ignore macros.
+ if (Var->getLocStart().isMacroID())
+ return;
+
+ // Only allow variable accesses for now, no function calls or member exprs.
+ // Check that we don't dereference the variable anywhere within the if. This
+ // avoids false positives for checks of the pointer for nullptr before it is
+ // dereferenced. If there is a dereferencing operator on this variable don't
+ // emit a diagnostic. Also ignore array subscripts.
+ const Decl *D = Var->getDecl();
+ auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
+ if (!match(findAll(
+ unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
+ *If, *Result.Context)
+ .empty() ||
+ !match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
+ *Result.Context)
+ .empty() ||
+ // FIXME: We should still warn if the paremater is implicitly converted to
+ // bool.
+ !match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
+ *If, *Result.Context)
+ .empty() ||
+ !match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
+ *If, *Result.Context)
+ .empty())
+ return;
+
+ diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did "
+ "you mean to dereference it?")
+ << FixItHint::CreateInsertion(Var->getLocStart(), "*");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- BoolPointerImplicitConversionCheck.h - clang-tidy ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for conditions based on implicit conversion from a bool pointer to
+/// bool.
+///
+/// Example:
+///
+/// \code
+/// bool *p;
+/// if (p) {
+/// // Never used in a pointer-specific way.
+/// }
+/// \endcode
+class BoolPointerImplicitConversionCheck : public ClangTidyCheck {
+public:
+ BoolPointerImplicitConversionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyMiscModule
+ ArgumentCommentCheck.cpp
+ AssertSideEffectCheck.cpp
+ ForwardingReferenceOverloadCheck.cpp
+ LambdaFunctionNameCheck.cpp
+ MisplacedConstCheck.cpp
+ UnconventionalAssignOperatorCheck.cpp
+ BoolPointerImplicitConversionCheck.cpp
+ DanglingHandleCheck.cpp
+ DefinitionsInHeadersCheck.cpp
+ FoldInitTypeCheck.cpp
+ ForwardDeclarationNamespaceCheck.cpp
+ InaccurateEraseCheck.cpp
+ IncorrectRoundings.cpp
+ InefficientAlgorithmCheck.cpp
+ MacroParenthesesCheck.cpp
+ MacroRepeatedSideEffectsCheck.cpp
+ MiscTidyModule.cpp
+ MisplacedWideningCastCheck.cpp
+ MoveConstantArgumentCheck.cpp
+ MoveConstructorInitCheck.cpp
+ MoveForwardingReferenceCheck.cpp
+ MultipleStatementMacroCheck.cpp
+ NewDeleteOverloadsCheck.cpp
+ NoexceptMoveConstructorCheck.cpp
+ NonCopyableObjects.cpp
+ RedundantExpressionCheck.cpp
+ SizeofContainerCheck.cpp
+ SizeofExpressionCheck.cpp
+ StaticAssertCheck.cpp
+ StringCompareCheck.cpp
+ StringConstructorCheck.cpp
+ StringIntegerAssignmentCheck.cpp
+ StringLiteralWithEmbeddedNulCheck.cpp
+ SuspiciousEnumUsageCheck.cpp
+ SuspiciousMissingCommaCheck.cpp
+ SuspiciousSemicolonCheck.cpp
+ SuspiciousStringCompareCheck.cpp
+ SwappedArgumentsCheck.cpp
+ ThrowByValueCatchByReferenceCheck.cpp
+ UndelegatedConstructor.cpp
+ UniqueptrResetReleaseCheck.cpp
+ UnusedAliasDeclsCheck.cpp
+ UnusedParametersCheck.cpp
+ UnusedRAIICheck.cpp
+ UnusedUsingDeclsCheck.cpp
+ UseAfterMoveCheck.cpp
+ VirtualNearMissCheck.cpp
+
+ LINK_LIBS
+ clangAnalysis
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ clangTooling
+ )
--- /dev/null
+//===--- DanglingHandleCheck.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 "DanglingHandleCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+ast_matchers::internal::BindableMatcher<Stmt>
+handleFrom(const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle,
+ const ast_matchers::internal::Matcher<Expr> &Arg) {
+ return cxxConstructExpr(hasDeclaration(cxxMethodDecl(ofClass(IsAHandle))),
+ hasArgument(0, Arg));
+}
+
+ast_matchers::internal::Matcher<Stmt> handleFromTemporaryValue(
+ const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
+ // If a ternary operator returns a temporary value, then both branches hold a
+ // temporary value. If one of them is not a temporary then it must be copied
+ // into one to satisfy the type of the operator.
+ const auto TemporaryTernary =
+ conditionalOperator(hasTrueExpression(cxxBindTemporaryExpr()),
+ hasFalseExpression(cxxBindTemporaryExpr()));
+
+ return handleFrom(IsAHandle, anyOf(cxxBindTemporaryExpr(), TemporaryTernary));
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isASequence() {
+ return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+ "::std::vector");
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isASet() {
+ return hasAnyName("::std::set", "::std::multiset", "::std::unordered_set",
+ "::std::unordered_multiset");
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isAMap() {
+ return hasAnyName("::std::map", "::std::multimap", "::std::unordered_map",
+ "::std::unordered_multimap");
+}
+
+ast_matchers::internal::BindableMatcher<Stmt> makeContainerMatcher(
+ const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
+ // This matcher could be expanded to detect:
+ // - Constructors: eg. vector<string_view>(3, string("A"));
+ // - emplace*(): This requires a different logic to determine that
+ // the conversion will happen inside the container.
+ // - map's insert: This requires detecting that the pair conversion triggers
+ // the bug. A little more complicated than what we have now.
+ return callExpr(
+ hasAnyArgument(
+ ignoringParenImpCasts(handleFromTemporaryValue(IsAHandle))),
+ anyOf(
+ // For sequences: assign, push_back, resize.
+ cxxMemberCallExpr(
+ callee(functionDecl(hasAnyName("assign", "push_back", "resize"))),
+ on(expr(hasType(recordDecl(isASequence()))))),
+ // For sequences and sets: insert.
+ cxxMemberCallExpr(
+ callee(functionDecl(hasName("insert"))),
+ on(expr(hasType(recordDecl(anyOf(isASequence(), isASet())))))),
+ // For maps: operator[].
+ cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(isAMap()))),
+ hasOverloadedOperatorName("[]"))));
+}
+
+} // anonymous namespace
+
+DanglingHandleCheck::DanglingHandleCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ HandleClasses(utils::options::parseStringList(Options.get(
+ "HandleClasses",
+ "std::basic_string_view;std::experimental::basic_string_view"))),
+ IsAHandle(cxxRecordDecl(hasAnyName(std::vector<StringRef>(
+ HandleClasses.begin(), HandleClasses.end())))
+ .bind("handle")) {}
+
+void DanglingHandleCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "HandleClasses",
+ utils::options::serializeStringList(HandleClasses));
+}
+
+void DanglingHandleCheck::registerMatchersForVariables(MatchFinder *Finder) {
+ const auto ConvertedHandle = handleFromTemporaryValue(IsAHandle);
+
+ // Find 'Handle foo(ReturnsAValue());'
+ Finder->addMatcher(
+ varDecl(hasType(cxxRecordDecl(IsAHandle)),
+ hasInitializer(
+ exprWithCleanups(has(ignoringParenImpCasts(ConvertedHandle)))
+ .bind("bad_stmt"))),
+ this);
+
+ // Find 'Handle foo = ReturnsAValue();'
+ Finder->addMatcher(
+ varDecl(
+ hasType(cxxRecordDecl(IsAHandle)), unless(parmVarDecl()),
+ hasInitializer(exprWithCleanups(has(ignoringParenImpCasts(handleFrom(
+ IsAHandle, ConvertedHandle))))
+ .bind("bad_stmt"))),
+ this);
+ // Find 'foo = ReturnsAValue(); // foo is Handle'
+ Finder->addMatcher(
+ cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(IsAHandle))),
+ hasOverloadedOperatorName("="),
+ hasArgument(1, ConvertedHandle))
+ .bind("bad_stmt"),
+ this);
+
+ // Container insertions that will dangle.
+ Finder->addMatcher(makeContainerMatcher(IsAHandle).bind("bad_stmt"), this);
+}
+
+void DanglingHandleCheck::registerMatchersForReturn(MatchFinder *Finder) {
+ // Return a local.
+ Finder->addMatcher(
+ returnStmt(
+ // The AST contains two constructor calls:
+ // 1. Value to Handle conversion.
+ // 2. Handle copy construction.
+ // We have to match both.
+ has(ignoringImplicit(handleFrom(
+ IsAHandle,
+ handleFrom(IsAHandle, declRefExpr(to(varDecl(
+ // Is function scope ...
+ hasAutomaticStorageDuration(),
+ // ... and it is a local array or Value.
+ anyOf(hasType(arrayType()),
+ hasType(recordDecl(
+ unless(IsAHandle))))))))))),
+ // Temporary fix for false positives inside lambdas.
+ unless(hasAncestor(lambdaExpr())))
+ .bind("bad_stmt"),
+ this);
+
+ // Return a temporary.
+ Finder->addMatcher(
+ returnStmt(
+ has(ignoringParenImpCasts(exprWithCleanups(has(ignoringParenImpCasts(
+ handleFrom(IsAHandle, handleFromTemporaryValue(IsAHandle))))))))
+ .bind("bad_stmt"),
+ this);
+}
+
+void DanglingHandleCheck::registerMatchers(MatchFinder *Finder) {
+ registerMatchersForVariables(Finder);
+ registerMatchersForReturn(Finder);
+}
+
+void DanglingHandleCheck::check(const MatchFinder::MatchResult &Result) {
+ auto *Handle = Result.Nodes.getNodeAs<CXXRecordDecl>("handle");
+ diag(Result.Nodes.getNodeAs<Stmt>("bad_stmt")->getLocStart(),
+ "%0 outlives its value")
+ << Handle->getQualifiedNameAsString();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DanglingHandleCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect dangling references in value handlers like
+/// std::experimental::string_view.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-dangling-handle.html
+class DanglingHandleCheck : public ClangTidyCheck {
+public:
+ DanglingHandleCheck(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:
+ void registerMatchersForVariables(ast_matchers::MatchFinder *Finder);
+ void registerMatchersForReturn(ast_matchers::MatchFinder *Finder);
+
+ const std::vector<std::string> HandleClasses;
+ const ast_matchers::internal::Matcher<RecordDecl> IsAHandle;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
--- /dev/null
+//===--- DefinitionsInHeadersCheck.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 "DefinitionsInHeadersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
+ utils::HeaderFileExtensionsSet, HeaderFileExtensions) {
+ return utils::isExpansionLocInHeaderFile(
+ Node.getLocStart(), Finder->getASTContext().getSourceManager(),
+ HeaderFileExtensions);
+}
+
+} // namespace
+
+DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
+ RawStringHeaderFileExtensions(
+ Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+ if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions, ',')) {
+ // FIXME: Find a more suitable way to handle invalid configuration
+ // options.
+ llvm::errs() << "Invalid header file extension: "
+ << RawStringHeaderFileExtensions << "\n";
+ }
+}
+
+void DefinitionsInHeadersCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
+ Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ auto DefinitionMatcher =
+ anyOf(functionDecl(isDefinition(), unless(isDeleted())),
+ varDecl(isDefinition()));
+ if (UseHeaderFileExtension) {
+ Finder->addMatcher(namedDecl(DefinitionMatcher,
+ usesHeaderFileExtension(HeaderFileExtensions))
+ .bind("name-decl"),
+ this);
+ } else {
+ Finder->addMatcher(
+ namedDecl(DefinitionMatcher,
+ anyOf(usesHeaderFileExtension(HeaderFileExtensions),
+ unless(isExpansionInMainFile())))
+ .bind("name-decl"),
+ this);
+ }
+}
+
+void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
+ // Don't run the check in failing TUs.
+ if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+ return;
+
+ // C++ [basic.def.odr] p6:
+ // There can be more than one definition of a class type, enumeration type,
+ // inline function with external linkage, class template, non-static function
+ // template, static data member of a class template, member function of a
+ // class template, or template specialization for which some template
+ // parameters are not specifiedin a program provided that each definition
+ // appears in a different translation unit, and provided the definitions
+ // satisfy the following requirements.
+ const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
+ assert(ND);
+ if (ND->isInvalidDecl())
+ return;
+
+ // Internal linkage variable definitions are ignored for now:
+ // const int a = 1;
+ // static int b = 1;
+ //
+ // Although these might also cause ODR violations, we can be less certain and
+ // should try to keep the false-positive rate down.
+ if (ND->getLinkageInternal() == InternalLinkage)
+ return;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ // Inline functions are allowed.
+ if (FD->isInlined())
+ return;
+ // Function templates are allowed.
+ if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
+ return;
+ // Ignore instantiated functions.
+ if (FD->isTemplateInstantiation())
+ return;
+ // Member function of a class template and member function of a nested class
+ // in a class template are allowed.
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const auto *DC = MD->getDeclContext();
+ while (DC->isRecord()) {
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
+ if (isa<ClassTemplatePartialSpecializationDecl>(RD))
+ return;
+ if (RD->getDescribedClassTemplate())
+ return;
+ }
+ DC = DC->getParent();
+ }
+ }
+
+ bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
+ diag(FD->getLocation(),
+ "%select{function|full function template specialization}0 %1 defined "
+ "in a header file; function definitions in header files can lead to "
+ "ODR violations")
+ << is_full_spec << FD << FixItHint::CreateInsertion(
+ FD->getReturnTypeSourceRange().getBegin(), "inline ");
+ } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
+ // Static data members of a class template are allowed.
+ if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
+ return;
+ // Ignore instantiated static data members of classes.
+ if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
+ return;
+ // Ignore variable definition within function scope.
+ if (VD->hasLocalStorage() || VD->isStaticLocal())
+ return;
+ // Ignore inline variables.
+ if (VD->isInline())
+ return;
+
+ diag(VD->getLocation(),
+ "variable %0 defined in a header file; "
+ "variable definitions in header files can lead to ODR violations")
+ << VD;
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DefinitionsInHeadersCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds non-extern non-inline function and variable definitions in header
+/// files, which can lead to potential ODR violations.
+///
+/// The check supports these options:
+/// - `UseHeaderFileExtension`: Whether to use file extension to distinguish
+/// header files. True by default.
+/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+/// header files (The filename extension should not contain "." prefix).
+/// ",h,hh,hpp,hxx" by default.
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between "," if there are other filename extensions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-definitions-in-headers.html
+class DefinitionsInHeadersCheck : public ClangTidyCheck {
+public:
+ DefinitionsInHeadersCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool UseHeaderFileExtension;
+ const std::string RawStringHeaderFileExtensions;
+ utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
--- /dev/null
+//===--- FoldInitTypeCheck.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 "FoldInitTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) {
+ // We match functions of interest and bind the iterator and init value types.
+ // Note: Right now we check only builtin types.
+ const auto BuiltinTypeWithId = [](const char *ID) {
+ return hasCanonicalType(builtinType().bind(ID));
+ };
+ const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) {
+ return anyOf(
+ // Pointer types.
+ pointsTo(BuiltinTypeWithId(ID)),
+ // Iterator types.
+ recordType(hasDeclaration(has(typedefNameDecl(
+ hasName("value_type"), hasType(BuiltinTypeWithId(ID)))))));
+ };
+
+ const auto IteratorParam = parmVarDecl(
+ hasType(hasCanonicalType(IteratorWithValueType("IterValueType"))));
+ const auto Iterator2Param = parmVarDecl(
+ hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType"))));
+ const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType")));
+
+ // std::accumulate, std::reduce.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::accumulate", "::std::reduce"),
+ hasParameter(0, IteratorParam), hasParameter(2, InitParam))),
+ argumentCountIs(3))
+ .bind("Call"),
+ this);
+ // std::inner_product.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::inner_product"),
+ hasParameter(0, IteratorParam),
+ hasParameter(2, Iterator2Param),
+ hasParameter(3, InitParam))),
+ argumentCountIs(4))
+ .bind("Call"),
+ this);
+ // std::reduce with a policy.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::reduce"),
+ hasParameter(1, IteratorParam),
+ hasParameter(3, InitParam))),
+ argumentCountIs(4))
+ .bind("Call"),
+ this);
+ // std::inner_product with a policy.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::std::inner_product"),
+ hasParameter(1, IteratorParam),
+ hasParameter(3, Iterator2Param),
+ hasParameter(4, InitParam))),
+ argumentCountIs(5))
+ .bind("Call"),
+ this);
+}
+
+/// Returns true if ValueType is allowed to fold into InitType, i.e. if:
+/// static_cast<InitType>(ValueType{some_value})
+/// does not result in trucation.
+static bool isValidBuiltinFold(const BuiltinType &ValueType,
+ const BuiltinType &InitType,
+ const ASTContext &Context) {
+ const auto ValueTypeSize = Context.getTypeSize(&ValueType);
+ const auto InitTypeSize = Context.getTypeSize(&InitType);
+ // It's OK to fold a float into a float of bigger or equal size, but not OK to
+ // fold into an int.
+ if (ValueType.isFloatingPoint())
+ return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize;
+ // It's OK to fold an int into:
+ // - an int of the same size and signedness.
+ // - a bigger int, regardless of signedness.
+ // - FIXME: should it be a warning to fold into floating point?
+ if (ValueType.isInteger()) {
+ if (InitType.isInteger()) {
+ if (InitType.isSignedInteger() == ValueType.isSignedInteger())
+ return InitTypeSize >= ValueTypeSize;
+ return InitTypeSize > ValueTypeSize;
+ }
+ if (InitType.isFloatingPoint())
+ return InitTypeSize >= ValueTypeSize;
+ }
+ return false;
+}
+
+/// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see
+// isValidBuiltinFold for details).
+void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType,
+ const BuiltinType &InitType,
+ const ASTContext &Context,
+ const CallExpr &CallNode) {
+ if (!isValidBuiltinFold(IterValueType, InitType, Context)) {
+ diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in "
+ "loss of precision")
+ << IterValueType.desugar() << InitType.desugar();
+ }
+}
+
+void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) {
+ // Given the iterator and init value type retreived by the matchers,
+ // we check that the ::value_type of the iterator is compatible with
+ // the init value type.
+ const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType");
+ const auto *IterValueType =
+ Result.Nodes.getNodeAs<BuiltinType>("IterValueType");
+ assert(InitType != nullptr);
+ assert(IterValueType != nullptr);
+
+ const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call");
+ assert(CallNode != nullptr);
+
+ doCheck(*IterValueType, *InitType, *Result.Context, *CallNode);
+
+ if (const auto *Iter2ValueType =
+ Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType"))
+ doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- FoldInitTypeCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find and flag invalid initializer values in folds, e.g. std::accumulate.
+/// Example:
+/// \code
+/// auto v = {65536L * 65536 * 65536};
+/// std::accumulate(begin(v), end(v), 0 /* int type is too small */);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-fold-init-type.html
+class FoldInitTypeCheck : public ClangTidyCheck {
+public:
+ FoldInitTypeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void doCheck(const BuiltinType &IterValueType, const BuiltinType &InitType,
+ const ASTContext &Context, const CallExpr &CallNode);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
--- /dev/null
+//===--- ForwardDeclarationNamespaceCheck.cpp - clang-tidy ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include <stack>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+ // Match all class declarations/definitions *EXCEPT*
+ // 1. implicit classes, e.g. `class A {};` has implicit `class A` inside `A`.
+ // 2. nested classes declared/defined inside another class.
+ // 3. template class declaration, template instantiation or
+ // specialization (NOTE: extern specialization is filtered out by
+ // `unless(hasAncestor(cxxRecordDecl()))`).
+ auto IsInSpecialization = hasAncestor(
+ decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+ functionDecl(isExplicitTemplateSpecialization()))));
+ Finder->addMatcher(
+ cxxRecordDecl(
+ hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
+ unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
+ unless(isInstantiated()), unless(IsInSpecialization),
+ unless(classTemplateSpecializationDecl()))
+ .bind("record_decl"),
+ this);
+
+ // Match all friend declarations. Classes used in friend declarations are not
+ // marked as referenced in AST. We need to record all record classes used in
+ // friend declarations.
+ Finder->addMatcher(friendDecl().bind("friend_decl"), this);
+}
+
+void ForwardDeclarationNamespaceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *RecordDecl =
+ Result.Nodes.getNodeAs<CXXRecordDecl>("record_decl")) {
+ StringRef DeclName = RecordDecl->getName();
+ if (RecordDecl->isThisDeclarationADefinition()) {
+ DeclNameToDefinitions[DeclName].push_back(RecordDecl);
+ } else {
+ // If a declaration has no definition, the definition could be in another
+ // namespace (a wrong namespace).
+ // NOTE: even a declaration does have definition, we still need it to
+ // compare with other declarations.
+ DeclNameToDeclarations[DeclName].push_back(RecordDecl);
+ }
+ } else {
+ const auto *Decl = Result.Nodes.getNodeAs<FriendDecl>("friend_decl");
+ assert(Decl && "Decl is neither record_decl nor friend decl!");
+
+ // Classes used in friend delarations are not marked referenced in AST,
+ // so we need to check classes used in friend declarations manually to
+ // reduce the rate of false positive.
+ // For example, in
+ // \code
+ // struct A;
+ // struct B { friend A; };
+ // \endcode
+ // `A` will not be marked as "referenced" in the AST.
+ if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
+ QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
+ FriendTypes.insert(Desugared.getTypePtr());
+ }
+ }
+}
+
+static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1,
+ const CXXRecordDecl *Decl2) {
+ const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
+ const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
+
+ // Since we only matched declarations whose parent is Namespace or
+ // TranslationUnit declaration, the parent should be either a translation unit
+ // or namespace.
+ if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
+ ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
+ return ParentDecl1 == ParentDecl2;
+ }
+ assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
+ "ParentDecl1 declaration must be a namespace");
+ assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
+ "ParentDecl2 declaration must be a namespace");
+ auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
+ auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
+ return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
+}
+
+static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
+ const auto *ParentDecl = Decl->getLexicalParent();
+ if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
+ return "(global)";
+ }
+ const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
+ std::string Ns;
+ llvm::raw_string_ostream OStream(Ns);
+ NsDecl->printQualifiedName(OStream);
+ OStream.flush();
+ return Ns.empty() ? "(global)" : Ns;
+}
+
+void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
+ // Iterate each group of declarations by name.
+ for (const auto &KeyValuePair : DeclNameToDeclarations) {
+ const auto &Declarations = KeyValuePair.second;
+ // If more than 1 declaration exists, we check if all are in the same
+ // namespace.
+ for (const auto *CurDecl : Declarations) {
+ if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
+ continue; // Skip forward declarations that are used/referenced.
+ }
+ if (FriendTypes.count(CurDecl->getTypeForDecl()) != 0) {
+ continue; // Skip forward declarations referenced as friend.
+ }
+ if (CurDecl->getLocation().isMacroID() ||
+ CurDecl->getLocation().isInvalid()) {
+ continue;
+ }
+ // Compare with all other declarations with the same name.
+ for (const auto *Decl : Declarations) {
+ if (Decl == CurDecl) {
+ continue; // Don't compare with self.
+ }
+ if (!CurDecl->hasDefinition() &&
+ !haveSameNamespaceOrTranslationUnit(CurDecl, Decl)) {
+ diag(CurDecl->getLocation(),
+ "declaration %0 is never referenced, but a declaration with "
+ "the same name found in another namespace '%1'")
+ << CurDecl << getNameOfNamespace(Decl);
+ diag(Decl->getLocation(), "a declaration of %0 is found here",
+ DiagnosticIDs::Note)
+ << Decl;
+ break; // FIXME: We only generate one warning for each declaration.
+ }
+ }
+ // Check if a definition in another namespace exists.
+ const auto DeclName = CurDecl->getName();
+ if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
+ continue; // No definition in this translation unit, we can skip it.
+ }
+ // Make a warning for each definition with the same name (in other
+ // namespaces).
+ const auto &Definitions = DeclNameToDefinitions[DeclName];
+ for (const auto *Def : Definitions) {
+ diag(CurDecl->getLocation(),
+ "no definition found for %0, but a definition with "
+ "the same name %1 found in another namespace '%2'")
+ << CurDecl << Def << getNameOfNamespace(Def);
+ diag(Def->getLocation(), "a definition of %0 is found here",
+ DiagnosticIDs::Note)
+ << Def;
+ }
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ForwardDeclarationNamespaceCheck.h - clang-tidy --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <set>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks if an unused forward declaration is in a wrong namespace.
+///
+/// The check inspects all unused forward declarations and checks if there is
+/// any declaration/definition with the same name, which could indicate
+/// that the forward declaration is potentially in a wrong namespace.
+///
+/// \code
+/// namespace na { struct A; }
+/// namespace nb { struct A {} };
+/// nb::A a;
+/// // warning : no definition found for 'A', but a definition with the same
+/// name 'A' found in another namespace 'nb::'
+/// \endcode
+///
+/// This check can only generate warnings, but it can't suggest fixes at this
+/// point.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forward-declaration-namespace.html
+class ForwardDeclarationNamespaceCheck : public ClangTidyCheck {
+public:
+ ForwardDeclarationNamespaceCheck(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;
+
+private:
+ llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDefinitions;
+ llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDeclarations;
+ llvm::SmallPtrSet<const Type *, 16> FriendTypes;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
--- /dev/null
+//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardingReferenceOverloadCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+// Check if the given type is related to std::enable_if.
+AST_MATCHER(QualType, isEnableIf) {
+ auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
+ if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
+ return false;
+ }
+ const NamedDecl *TypeDecl =
+ Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
+ return TypeDecl->isInStdNamespace() &&
+ (TypeDecl->getName().equals("enable_if") ||
+ TypeDecl->getName().equals("enable_if_t"));
+ };
+ const Type *BaseType = Node.getTypePtr();
+ // Case: pointer or reference to enable_if.
+ while (BaseType->isPointerType() || BaseType->isReferenceType()) {
+ BaseType = BaseType->getPointeeType().getTypePtr();
+ }
+ // Case: type parameter dependent (enable_if<is_integral<T>>).
+ if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
+ BaseType = Dependent->getQualifier()->getAsType();
+ }
+ if (!BaseType)
+ return false;
+ if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
+ return true; // Case: enable_if_t< >.
+ } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
+ if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
+ if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
+ return true; // Case: enable_if< >::type.
+ }
+ }
+ }
+ return false;
+}
+AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
+ clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
+ return Node.hasDefaultArgument() &&
+ TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
+}
+} // namespace
+
+void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
+ // Forwarding references require C++11 or later.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ auto ForwardingRefParm =
+ parmVarDecl(
+ hasType(qualType(rValueReferenceType(),
+ references(templateTypeParmType(hasDeclaration(
+ templateTypeParmDecl().bind("type-parm-decl")))),
+ unless(references(isConstQualified())))))
+ .bind("parm-var");
+
+ DeclarationMatcher findOverload =
+ cxxConstructorDecl(
+ hasParameter(0, ForwardingRefParm),
+ unless(hasAnyParameter(
+ // No warning: enable_if as constructor parameter.
+ parmVarDecl(hasType(isEnableIf())))),
+ unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
+ // No warning: enable_if as type parameter.
+ hasDefaultArgument(isEnableIf())))))))
+ .bind("ctor");
+ Finder->addMatcher(findOverload, this);
+}
+
+void ForwardingReferenceOverloadCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+ const auto *TypeParmDecl =
+ Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+ // Get the FunctionDecl and FunctionTemplateDecl containing the function
+ // parameter.
+ const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+ if (!FuncForParam)
+ return;
+ const FunctionTemplateDecl *FuncTemplate =
+ FuncForParam->getDescribedFunctionTemplate();
+ if (!FuncTemplate)
+ return;
+
+ // Check that the template type parameter belongs to the same function
+ // template as the function parameter of that type. (This implies that type
+ // deduction will happen on the type.)
+ const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+ if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
+ return;
+
+ // Every parameter after the first must have a default value.
+ const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+ for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
+ if (!(*Iter)->hasDefaultArg())
+ return;
+ }
+ bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
+ DisabledMove = false;
+ for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
+ if (OtherCtor->isCopyOrMoveConstructor()) {
+ if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
+ (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
+ else
+ (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
+ }
+ }
+ bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
+ bool Move = !DisabledMove || EnabledMove;
+ if (!Copy && !Move)
+ return;
+ diag(Ctor->getLocation(),
+ "constructor accepting a forwarding reference can "
+ "hide the %select{copy|move|copy and move}0 constructor%s1")
+ << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
+ for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
+ if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
+ OtherCtor->getAccess() != AS_private) {
+ diag(OtherCtor->getLocation(),
+ "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
+ << OtherCtor->isMoveConstructor();
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ForwardingReferenceOverloadCheck.h - clang-tidy---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The checker looks for constructors that can act as copy or move constructors
+/// through their forwarding reference parameters. If a non const lvalue
+/// reference is passed to the constructor, the forwarding reference parameter
+/// can be a perfect match while the const reference parameter of the copy
+/// constructor can't. The forwarding reference constructor will be called,
+/// which can lead to confusion.
+/// For detailed description of this problem see: Scott Meyers, Effective Modern
+/// C++ Design, item 26.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html
+class ForwardingReferenceOverloadCheck : public ClangTidyCheck {
+public:
+ ForwardingReferenceOverloadCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
--- /dev/null
+//===--- InaccurateEraseCheck.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 "InaccurateEraseCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
+}
+
+void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto EndCall =
+ callExpr(
+ callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))),
+ hasArgument(
+ 1,
+ anyOf(cxxConstructExpr(has(ignoringImplicit(
+ cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
+ .bind("end")))),
+ anything())))
+ .bind("alg");
+
+ const auto DeclInStd = decl(isInStdNamespace());
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))),
+ callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
+ hasArgument(0, has(ignoringImplicit(
+ anyOf(EndCall, has(ignoringImplicit(EndCall)))))),
+ unless(isInTemplateInstantiation()))
+ .bind("erase"),
+ this);
+}
+
+void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemberCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
+ const auto *EndExpr =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("end");
+ const SourceLocation Loc = MemberCall->getLocStart();
+
+ FixItHint Hint;
+
+ if (!Loc.isMacroID() && EndExpr) {
+ const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("alg");
+ std::string ReplacementText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ AlgCall->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+ Hint = FixItHint::CreateInsertion(EndLoc, ", " + ReplacementText);
+ }
+
+ diag(Loc, "this call will remove at most one item even when multiple items "
+ "should be removed")
+ << Hint;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- InaccurateEraseCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for inaccurate use of the `erase()` method.
+///
+/// Algorithms like `remove()` do not actually remove any element from the
+/// container but return an iterator to the first redundant element at the end
+/// of the container. These redundant elements must be removed using the
+/// `erase()` method. This check warns when not all of the elements will be
+/// removed due to using an inappropriate overload.
+class InaccurateEraseCheck : public ClangTidyCheck {
+public:
+ InaccurateEraseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
--- /dev/null
+//===--- IncorrectRoundings.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 "IncorrectRoundings.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(FloatingLiteral, floatHalf) {
+ const auto &literal = Node.getValue();
+ if ((&Node.getSemantics()) == &llvm::APFloat::IEEEsingle())
+ return literal.convertToFloat() == 0.5f;
+ if ((&Node.getSemantics()) == &llvm::APFloat::IEEEdouble())
+ return literal.convertToDouble() == 0.5;
+ return false;
+}
+} // namespace
+
+void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) {
+ // Match a floating literal with value 0.5.
+ auto FloatHalf = floatLiteral(floatHalf());
+
+ // Match a floating point expression.
+ auto FloatType = expr(hasType(realFloatingPointType()));
+
+ // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
+ // cast to floating type.
+ auto FloatOrCastHalf =
+ anyOf(FloatHalf,
+ implicitCastExpr(FloatType, has(ignoringParenImpCasts(FloatHalf))));
+
+ // Match if either the LHS or RHS is a floating literal of 0.5 or a floating
+ // literal of 0.5 and the other is of type double or vice versa.
+ auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
+ allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));
+
+ // Find expressions of cast to int of the sum of a floating point expression
+ // and 0.5.
+ MatchFinder->addMatcher(
+ implicitCastExpr(
+ hasImplicitDestinationType(isInteger()),
+ ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
+ .bind("CastExpr"),
+ this);
+}
+
+void IncorrectRoundings::check(const MatchFinder::MatchResult &Result) {
+ const auto *CastExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("CastExpr");
+ diag(CastExpr->getLocStart(),
+ "casting (double + 0.5) to integer leads to incorrect rounding; "
+ "consider using lround (#include <cmath>) instead");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- IncorrectRoundings.h - clang-tidy ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Checks the usage of patterns known to produce incorrect rounding.
+/// Programmers often use
+/// (int)(double_expression + 0.5)
+/// to round the double expression to an integer. The problem with this
+/// 1. It is unnecessarily slow.
+/// 2. It is incorrect. The number 0.499999975 (smallest representable float
+/// number below 0.5) rounds to 1.0. Even worse behavior for negative
+/// numbers where both -0.5f and -1.4f both round to 0.0.
+class IncorrectRoundings : public ClangTidyCheck {
+public:
+ IncorrectRoundings(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
--- /dev/null
+//===--- InefficientAlgorithmCheck.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 "InefficientAlgorithmCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static bool areTypesCompatible(QualType Left, QualType Right) {
+ if (const auto *LeftRefType = Left->getAs<ReferenceType>())
+ Left = LeftRefType->getPointeeType();
+ if (const auto *RightRefType = Right->getAs<ReferenceType>())
+ Right = RightRefType->getPointeeType();
+ return Left->getCanonicalTypeUnqualified() ==
+ Right->getCanonicalTypeUnqualified();
+}
+
+void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto Algorithms =
+ hasAnyName("::std::find", "::std::count", "::std::equal_range",
+ "::std::lower_bound", "::std::upper_bound");
+ const auto ContainerMatcher = classTemplateSpecializationDecl(hasAnyName(
+ "::std::set", "::std::map", "::std::multiset", "::std::multimap",
+ "::std::unordered_set", "::std::unordered_map",
+ "::std::unordered_multiset", "::std::unordered_multimap"));
+
+ const auto Matcher =
+ callExpr(
+ callee(functionDecl(Algorithms)),
+ hasArgument(
+ 0, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+ callee(cxxMethodDecl(hasName("begin"))),
+ on(declRefExpr(
+ hasDeclaration(decl().bind("IneffContObj")),
+ anyOf(hasType(ContainerMatcher.bind("IneffCont")),
+ hasType(pointsTo(
+ ContainerMatcher.bind("IneffContPtr")))))
+ .bind("IneffContExpr"))))))),
+ hasArgument(
+ 1, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+ callee(cxxMethodDecl(hasName("end"))),
+ on(declRefExpr(
+ hasDeclaration(equalsBoundNode("IneffContObj"))))))))),
+ hasArgument(2, expr().bind("AlgParam")),
+ unless(isInTemplateInstantiation()))
+ .bind("IneffAlg");
+
+ Finder->addMatcher(Matcher, this);
+}
+
+void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
+ const auto *IneffCont =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
+ bool PtrToContainer = false;
+ if (!IneffCont) {
+ IneffCont =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
+ PtrToContainer = true;
+ }
+ const llvm::StringRef IneffContName = IneffCont->getName();
+ const bool Unordered =
+ IneffContName.find("unordered") != llvm::StringRef::npos;
+ const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos;
+
+ // Store if the key type of the container is compatible with the value
+ // that is searched for.
+ QualType ValueType = AlgCall->getArg(2)->getType();
+ QualType KeyType =
+ IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType();
+ const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType);
+
+ // Check if the comparison type for the algorithm and the container matches.
+ if (AlgCall->getNumArgs() == 4 && !Unordered) {
+ const Expr *Arg = AlgCall->getArg(3);
+ const QualType AlgCmp =
+ Arg->getType().getUnqualifiedType().getCanonicalType();
+ const unsigned CmpPosition =
+ (IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
+ const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
+ .getAsType()
+ .getUnqualifiedType()
+ .getCanonicalType();
+ if (AlgCmp != ContainerCmp) {
+ diag(Arg->getLocStart(),
+ "different comparers used in the algorithm and the container");
+ return;
+ }
+ }
+
+ const auto *AlgDecl = AlgCall->getDirectCallee();
+ if (!AlgDecl)
+ return;
+
+ if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
+ return;
+
+ const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
+ const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
+ FixItHint Hint;
+
+ SourceManager &SM = *Result.SourceManager;
+ LangOptions LangOpts = getLangOpts();
+
+ CharSourceRange CallRange =
+ CharSourceRange::getTokenRange(AlgCall->getSourceRange());
+
+ // FIXME: Create a common utility to extract a file range that the given token
+ // sequence is exactly spelled at (without macro argument expansions etc.).
+ // We can't use Lexer::makeFileCharRange here, because for
+ //
+ // #define F(x) x
+ // x(a b c);
+ //
+ // it will return "x(a b c)", when given the range "a"-"c". It makes sense for
+ // removals, but not for replacements.
+ //
+ // This code is over-simplified, but works for many real cases.
+ if (SM.isMacroArgExpansion(CallRange.getBegin()) &&
+ SM.isMacroArgExpansion(CallRange.getEnd())) {
+ CallRange.setBegin(SM.getSpellingLoc(CallRange.getBegin()));
+ CallRange.setEnd(SM.getSpellingLoc(CallRange.getEnd()));
+ }
+
+ if (!CallRange.getBegin().isMacroID() && !Maplike && CompatibleTypes) {
+ StringRef ContainerText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()), SM,
+ LangOpts);
+ StringRef ParamText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(AlgParam->getSourceRange()), SM,
+ LangOpts);
+ std::string ReplacementText =
+ (llvm::Twine(ContainerText) + (PtrToContainer ? "->" : ".") +
+ AlgDecl->getName() + "(" + ParamText + ")")
+ .str();
+ Hint = FixItHint::CreateReplacement(CallRange, ReplacementText);
+ }
+
+ diag(AlgCall->getLocStart(),
+ "this STL algorithm call should be replaced with a container method")
+ << Hint;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- InefficientAlgorithmCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Warns on inefficient use of STL algorithms on associative containers.
+///
+/// Associative containers implements some of the algorithms as methods which
+/// should be preferred to the algorithms in the algorithm header. The methods
+/// can take advanatage of the order of the elements.
+class InefficientAlgorithmCheck : public ClangTidyCheck {
+public:
+ InefficientAlgorithmCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
--- /dev/null
+//===--- LambdaFunctionNameCheck.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 "LambdaFunctionNameCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+// Keep track of macro expansions that contain both __FILE__ and __LINE__. If
+// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
+// warning because __FILE__ and __LINE__ may be useful even if __func__ or
+// __FUNCTION__ is not, especially if the macro could be used in the context of
+// either a function body or a lambda body.
+class MacroExpansionsWithFileAndLine : public PPCallbacks {
+public:
+ explicit MacroExpansionsWithFileAndLine(
+ LambdaFunctionNameCheck::SourceRangeSet *SME)
+ : SuppressMacroExpansions(SME) {}
+
+ void MacroExpands(const Token &MacroNameTok,
+ const MacroDefinition &MD, SourceRange Range,
+ const MacroArgs *Args) override {
+ bool has_file = false;
+ bool has_line = false;
+ for (const auto& T : MD.getMacroInfo()->tokens()) {
+ if (T.is(tok::identifier)) {
+ StringRef IdentName = T.getIdentifierInfo()->getName();
+ if (IdentName == "__FILE__") {
+ has_file = true;
+ } else if (IdentName == "__LINE__") {
+ has_line = true;
+ }
+ }
+ }
+ if (has_file && has_line) {
+ SuppressMacroExpansions->insert(Range);
+ }
+ }
+
+private:
+ LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
+};
+
+} // namespace
+
+void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
+ // Match on PredefinedExprs inside a lambda.
+ Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
+ this);
+}
+
+void LambdaFunctionNameCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ llvm::make_unique<MacroExpansionsWithFileAndLine>(
+ &SuppressMacroExpansions));
+}
+
+void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");
+ if (E->getIdentType() != PredefinedExpr::Func &&
+ E->getIdentType() != PredefinedExpr::Function) {
+ // We don't care about other PredefinedExprs.
+ return;
+ }
+ if (E->getLocation().isMacroID()) {
+ auto ER =
+ Result.SourceManager->getImmediateExpansionRange(E->getLocation());
+ if (SuppressMacroExpansions.find(SourceRange(ER.first, ER.second)) !=
+ SuppressMacroExpansions.end()) {
+ // This is a macro expansion for which we should not warn.
+ return;
+ }
+ }
+ diag(E->getLocation(),
+ "inside a lambda, '%0' expands to the name of the function call "
+ "operator; consider capturing the name of the enclosing function "
+ "explicitly")
+ << PredefinedExpr::getIdentTypeName(E->getIdentType());
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- LambdaFunctionNameCheck.h - clang-tidy------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect when __func__ or __FUNCTION__ is being used from within a lambda. In
+/// that context, those expressions expand to the name of the call operator
+/// (i.e., `operator()`).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html
+class LambdaFunctionNameCheck : public ClangTidyCheck {
+public:
+ struct SourceRangeLessThan {
+ bool operator()(const SourceRange &L, const SourceRange &R) const {
+ if (L.getBegin() == R.getBegin()) {
+ return L.getEnd() < R.getEnd();
+ }
+ return L.getBegin() < R.getBegin();
+ }
+ };
+ using SourceRangeSet = std::set<SourceRange, SourceRangeLessThan>;
+
+ LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ SourceRangeSet SuppressMacroExpansions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
--- /dev/null
+//===--- MacroParenthesesCheck.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 "MacroParenthesesCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+class MacroParenthesesPPCallbacks : public PPCallbacks {
+public:
+ MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check)
+ : PP(PP), Check(Check) {}
+
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override {
+ replacementList(MacroNameTok, MD->getMacroInfo());
+ argument(MacroNameTok, MD->getMacroInfo());
+ }
+
+private:
+ /// Replacement list with calculations should be enclosed in parentheses.
+ void replacementList(const Token &MacroNameTok, const MacroInfo *MI);
+
+ /// Arguments should be enclosed in parentheses.
+ void argument(const Token &MacroNameTok, const MacroInfo *MI);
+
+ Preprocessor *PP;
+ MacroParenthesesCheck *Check;
+};
+} // namespace
+
+/// Is argument surrounded properly with parentheses/braces/squares/commas?
+static bool isSurroundedLeft(const Token &T) {
+ return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
+ tok::semi);
+}
+
+/// Is argument surrounded properly with parentheses/braces/squares/commas?
+static bool isSurroundedRight(const Token &T) {
+ return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
+ tok::semi);
+}
+
+/// Is given TokenKind a keyword?
+static bool isKeyword(const Token &T) {
+ // FIXME: better matching of keywords to avoid false positives.
+ return T.isOneOf(tok::kw_case, tok::kw_const, tok::kw_struct);
+}
+
+/// Warning is written when one of these operators are not within parentheses.
+static bool isWarnOp(const Token &T) {
+ // FIXME: This is an initial list of operators. It can be tweaked later to
+ // get more positives or perhaps avoid some false positive.
+ return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
+ tok::amp, tok::pipe, tok::caret);
+}
+
+/// Is given Token a keyword that is used in variable declarations?
+static bool isVarDeclKeyword(const Token &T) {
+ return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int,
+ tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const,
+ tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct,
+ tok::kw_signed, tok::kw_unsigned);
+}
+
+/// Is there a possible variable declaration at Tok?
+static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok) {
+ if (Tok == MI->tokens_end())
+ return false;
+
+ // If we see int/short/struct/etc., just assume this is a variable
+ // declaration.
+ if (isVarDeclKeyword(*Tok))
+ return true;
+
+ // Variable declarations start with identifier or coloncolon.
+ if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
+ return false;
+
+ // Skip possible types, etc
+ while (Tok != MI->tokens_end() &&
+ Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
+ tok::star, tok::amp, tok::ampamp, tok::less,
+ tok::greater))
+ Tok++;
+
+ // Return true for possible variable declarations.
+ return Tok == MI->tokens_end() ||
+ Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
+ isVarDeclKeyword(*Tok);
+}
+
+void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok,
+ const MacroInfo *MI) {
+ // Make sure macro replacement isn't a variable declaration.
+ if (possibleVarDecl(MI, MI->tokens_begin()))
+ return;
+
+ // Count how deep we are in parentheses/braces/squares.
+ int Count = 0;
+
+ // SourceLocation for error
+ SourceLocation Loc;
+
+ for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
+ const Token &Tok = *TI;
+ // Replacement list contains keywords, don't warn about it.
+ if (isKeyword(Tok))
+ return;
+ // When replacement list contains comma/semi don't warn about it.
+ if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
+ return;
+ if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
+ ++Count;
+ } else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
+ --Count;
+ // If there are unbalanced parentheses don't write any warning
+ if (Count < 0)
+ return;
+ } else if (Count == 0 && isWarnOp(Tok)) {
+ // Heuristic for macros that are clearly not intended to be enclosed in
+ // parentheses, macro starts with operator. For example:
+ // #define X *10
+ if (TI == MI->tokens_begin() && (TI + 1) != TE &&
+ !Tok.isOneOf(tok::plus, tok::minus))
+ return;
+ // Don't warn about this macro if the last token is a star. For example:
+ // #define X void *
+ if ((TE - 1)->is(tok::star))
+ return;
+
+ Loc = Tok.getLocation();
+ }
+ }
+ if (Loc.isValid()) {
+ const Token &Last = *(MI->tokens_end() - 1);
+ Check->diag(Loc, "macro replacement list should be enclosed in parentheses")
+ << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(), "(")
+ << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
+ PP->getSpelling(Last).length()),
+ ")");
+ }
+}
+
+void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
+ const MacroInfo *MI) {
+
+ // Skip variable declaration.
+ bool VarDecl = possibleVarDecl(MI, MI->tokens_begin());
+
+ for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
+ // First token.
+ if (TI == MI->tokens_begin())
+ continue;
+
+ // Last token.
+ if ((TI + 1) == MI->tokens_end())
+ continue;
+
+ const Token &Prev = *(TI - 1);
+ const Token &Next = *(TI + 1);
+
+ const Token &Tok = *TI;
+
+ // There should not be extra parentheses in possible variable declaration.
+ if (VarDecl) {
+ if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
+ VarDecl = false;
+ continue;
+ }
+
+ // Only interested in identifiers.
+ if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
+ continue;
+
+ // Only interested in macro arguments.
+ if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
+ continue;
+
+ // Argument is surrounded with parentheses/squares/braces/commas.
+ if (isSurroundedLeft(Prev) && isSurroundedRight(Next))
+ continue;
+
+ // Don't warn after hash/hashhash or before hashhash.
+ if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
+ continue;
+
+ // Argument is a struct member.
+ if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
+ tok::periodstar))
+ continue;
+
+ // Argument is a namespace or class.
+ if (Next.is(tok::coloncolon))
+ continue;
+
+ // String concatenation.
+ if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
+ continue;
+
+ // Type/Var.
+ if (isAnyIdentifier(Prev.getKind()) || isKeyword(Prev) ||
+ isAnyIdentifier(Next.getKind()) || isKeyword(Next))
+ continue;
+
+ // Initialization.
+ if (Next.is(tok::l_paren))
+ continue;
+
+ // Cast.
+ if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
+ TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
+ continue;
+
+ // Assignment/return, i.e. '=x;' or 'return x;'.
+ if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
+ continue;
+
+ // C++ template parameters.
+ if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
+ Next.isOneOf(tok::comma, tok::greater))
+ continue;
+
+ // Namespaces.
+ if (Prev.is(tok::kw_namespace))
+ continue;
+
+ // Variadic templates
+ if (MI->isVariadic())
+ continue;
+
+ Check->diag(Tok.getLocation(), "macro argument should be enclosed in "
+ "parentheses")
+ << FixItHint::CreateInsertion(Tok.getLocation(), "(")
+ << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
+ PP->getSpelling(Tok).length()),
+ ")");
+ }
+}
+
+void MacroParenthesesCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ llvm::make_unique<MacroParenthesesPPCallbacks>(
+ &Compiler.getPreprocessor(), this));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MacroParenthesesCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds macros that can have unexpected behaviour due to missing parentheses.
+///
+/// Macros are expanded by the preprocessor as-is. As a result, there can be
+/// unexpected behaviour; operators may be evaluated in unexpected order and
+/// unary operators may become binary operators, etc.
+///
+/// When the replacement list has an expression, it is recommended to surround
+/// it with parentheses. This ensures that the macro result is evaluated
+/// completely before it is used.
+///
+/// It is also recommended to surround macro arguments in the replacement list
+/// with parentheses. This ensures that the argument value is calculated
+/// properly.
+class MacroParenthesesCheck : public ClangTidyCheck {
+public:
+ MacroParenthesesCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
--- /dev/null
+//===--- MacroRepeatedSideEffectsCheck.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 "MacroRepeatedSideEffectsCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/MacroArgs.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+class MacroRepeatedPPCallbacks : public PPCallbacks {
+public:
+ MacroRepeatedPPCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
+ : Check(Check), PP(PP) {}
+
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override;
+
+private:
+ ClangTidyCheck &Check;
+ Preprocessor &PP;
+
+ unsigned countArgumentExpansions(const MacroInfo *MI,
+ const IdentifierInfo *Arg) const;
+
+ bool hasSideEffects(const Token *ResultArgToks) const;
+};
+} // End of anonymous namespace.
+
+void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,
+ const MacroDefinition &MD,
+ SourceRange Range,
+ const MacroArgs *Args) {
+ // Ignore macro argument expansions.
+ if (!Range.getBegin().isFileID())
+ return;
+
+ const MacroInfo *MI = MD.getMacroInfo();
+
+ // Bail out if the contents of the macro are containing keywords that are
+ // making the macro too complex.
+ if (std::find_if(
+ MI->tokens().begin(), MI->tokens().end(), [](const Token &T) {
+ return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch,
+ tok::kw_case, tok::kw_break, tok::kw_while,
+ tok::kw_do, tok::kw_for, tok::kw_continue,
+ tok::kw_goto, tok::kw_return);
+ }) != MI->tokens().end())
+ return;
+
+ for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
+ const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
+ const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
+
+ if (hasSideEffects(ResultArgToks) &&
+ countArgumentExpansions(MI, Arg) >= 2) {
+ Check.diag(ResultArgToks->getLocation(),
+ "side effects in the %ordinal0 macro argument %1 are "
+ "repeated in macro expansion")
+ << (ArgNo + 1) << Arg;
+ Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",
+ DiagnosticIDs::Note)
+ << MacroNameTok.getIdentifierInfo();
+ }
+ }
+}
+
+unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
+ const MacroInfo *MI, const IdentifierInfo *Arg) const {
+ // Current argument count. When moving forward to a different control-flow
+ // path this can decrease.
+ unsigned Current = 0;
+ // Max argument count.
+ unsigned Max = 0;
+ bool SkipParen = false;
+ int SkipParenCount = 0;
+ // Has a __builtin_constant_p been found?
+ bool FoundBuiltin = false;
+ bool PrevTokenIsHash = false;
+ // Count when "?" is reached. The "Current" will get this value when the ":"
+ // is reached.
+ std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
+ for (const auto &T : MI->tokens()) {
+ // The result of __builtin_constant_p(x) is 0 if x is a macro argument
+ // with side effects. If we see a __builtin_constant_p(x) followed by a
+ // "?" "&&" or "||", then we need to reason about control flow to report
+ // warnings correctly. Until such reasoning is added, bail out when this
+ // happens.
+ if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
+ return Max;
+
+ // Skip stringified tokens.
+ if (T.is(tok::hash)) {
+ PrevTokenIsHash = true;
+ continue;
+ }
+ if (PrevTokenIsHash) {
+ PrevTokenIsHash = false;
+ continue;
+ }
+
+ // Handling of ? and :.
+ if (T.is(tok::question)) {
+ CountAtQuestion.push(Current);
+ } else if (T.is(tok::colon)) {
+ if (CountAtQuestion.empty())
+ return 0;
+ Current = CountAtQuestion.top();
+ CountAtQuestion.pop();
+ }
+
+ // If current token is a parenthesis, skip it.
+ if (SkipParen) {
+ if (T.is(tok::l_paren))
+ SkipParenCount++;
+ else if (T.is(tok::r_paren))
+ SkipParenCount--;
+ SkipParen = (SkipParenCount != 0);
+ if (SkipParen)
+ continue;
+ }
+
+ IdentifierInfo *TII = T.getIdentifierInfo();
+ // If not existent, skip it.
+ if (TII == nullptr)
+ continue;
+
+ // If a __builtin_constant_p is found within the macro definition, don't
+ // count arguments inside the parentheses and remember that it has been
+ // seen in case there are "?", "&&" or "||" operators later.
+ if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
+ FoundBuiltin = true;
+ SkipParen = true;
+ continue;
+ }
+
+ // If another macro is found within the macro definition, skip the macro
+ // and the eventual arguments.
+ if (TII->hasMacroDefinition()) {
+ const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();
+ if (M != nullptr && M->isFunctionLike())
+ SkipParen = true;
+ continue;
+ }
+
+ // Count argument.
+ if (TII == Arg) {
+ Current++;
+ if (Current > Max)
+ Max = Current;
+ }
+ }
+ return Max;
+}
+
+bool MacroRepeatedPPCallbacks::hasSideEffects(
+ const Token *ResultArgToks) const {
+ for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
+ if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
+ return true;
+ }
+ return false;
+}
+
+void MacroRepeatedSideEffectsCheck::registerPPCallbacks(
+ CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ ::llvm::make_unique<MacroRepeatedPPCallbacks>(
+ *this, Compiler.getPreprocessor()));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MacroRepeatedSideEffectsCheck.h - clang-tidy -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for repeated argument with side effects in macros.
+class MacroRepeatedSideEffectsCheck : public ClangTidyCheck {
+public:
+ MacroRepeatedSideEffectsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
--- /dev/null
+//===--- MiscTidyModule.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 "ArgumentCommentCheck.h"
+#include "AssertSideEffectCheck.h"
+#include "BoolPointerImplicitConversionCheck.h"
+#include "DanglingHandleCheck.h"
+#include "DefinitionsInHeadersCheck.h"
+#include "FoldInitTypeCheck.h"
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "ForwardingReferenceOverloadCheck.h"
+#include "InaccurateEraseCheck.h"
+#include "IncorrectRoundings.h"
+#include "InefficientAlgorithmCheck.h"
+#include "LambdaFunctionNameCheck.h"
+#include "MacroParenthesesCheck.h"
+#include "MacroRepeatedSideEffectsCheck.h"
+#include "MisplacedConstCheck.h"
+#include "MisplacedWideningCastCheck.h"
+#include "MoveConstantArgumentCheck.h"
+#include "MoveConstructorInitCheck.h"
+#include "MoveForwardingReferenceCheck.h"
+#include "MultipleStatementMacroCheck.h"
+#include "NewDeleteOverloadsCheck.h"
+#include "NoexceptMoveConstructorCheck.h"
+#include "NonCopyableObjects.h"
+#include "RedundantExpressionCheck.h"
+#include "SizeofContainerCheck.h"
+#include "SizeofExpressionCheck.h"
+#include "StaticAssertCheck.h"
+#include "StringCompareCheck.h"
+#include "StringConstructorCheck.h"
+#include "StringIntegerAssignmentCheck.h"
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "SuspiciousEnumUsageCheck.h"
+#include "SuspiciousMissingCommaCheck.h"
+#include "SuspiciousSemicolonCheck.h"
+#include "SuspiciousStringCompareCheck.h"
+#include "SwappedArgumentsCheck.h"
+#include "ThrowByValueCatchByReferenceCheck.h"
+#include "UnconventionalAssignOperatorCheck.h"
+#include "UndelegatedConstructor.h"
+#include "UniqueptrResetReleaseCheck.h"
+#include "UnusedAliasDeclsCheck.h"
+#include "UnusedParametersCheck.h"
+#include "UnusedRAIICheck.h"
+#include "UnusedUsingDeclsCheck.h"
+#include "UseAfterMoveCheck.h"
+#include "VirtualNearMissCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class MiscModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
+ CheckFactories.registerCheck<AssertSideEffectCheck>(
+ "misc-assert-side-effect");
+ CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
+ "misc-forwarding-reference-overload");
+ CheckFactories.registerCheck<LambdaFunctionNameCheck>(
+ "misc-lambda-function-name");
+ CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
+ CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
+ "misc-unconventional-assign-operator");
+ CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
+ "misc-bool-pointer-implicit-conversion");
+ CheckFactories.registerCheck<DanglingHandleCheck>("misc-dangling-handle");
+ CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
+ "misc-definitions-in-headers");
+ CheckFactories.registerCheck<FoldInitTypeCheck>("misc-fold-init-type");
+ CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
+ "misc-forward-declaration-namespace");
+ CheckFactories.registerCheck<InaccurateEraseCheck>("misc-inaccurate-erase");
+ CheckFactories.registerCheck<IncorrectRoundings>(
+ "misc-incorrect-roundings");
+ CheckFactories.registerCheck<InefficientAlgorithmCheck>(
+ "misc-inefficient-algorithm");
+ CheckFactories.registerCheck<MacroParenthesesCheck>(
+ "misc-macro-parentheses");
+ CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
+ "misc-macro-repeated-side-effects");
+ CheckFactories.registerCheck<MisplacedWideningCastCheck>(
+ "misc-misplaced-widening-cast");
+ CheckFactories.registerCheck<MoveConstantArgumentCheck>(
+ "misc-move-const-arg");
+ CheckFactories.registerCheck<MoveConstructorInitCheck>(
+ "misc-move-constructor-init");
+ CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
+ "misc-move-forwarding-reference");
+ CheckFactories.registerCheck<MultipleStatementMacroCheck>(
+ "misc-multiple-statement-macro");
+ CheckFactories.registerCheck<NewDeleteOverloadsCheck>(
+ "misc-new-delete-overloads");
+ CheckFactories.registerCheck<NoexceptMoveConstructorCheck>(
+ "misc-noexcept-move-constructor");
+ CheckFactories.registerCheck<NonCopyableObjectsCheck>(
+ "misc-non-copyable-objects");
+ CheckFactories.registerCheck<RedundantExpressionCheck>(
+ "misc-redundant-expression");
+ CheckFactories.registerCheck<SizeofContainerCheck>("misc-sizeof-container");
+ CheckFactories.registerCheck<SizeofExpressionCheck>(
+ "misc-sizeof-expression");
+ CheckFactories.registerCheck<StaticAssertCheck>("misc-static-assert");
+ CheckFactories.registerCheck<StringCompareCheck>("misc-string-compare");
+ CheckFactories.registerCheck<StringConstructorCheck>(
+ "misc-string-constructor");
+ CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
+ "misc-string-integer-assignment");
+ CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
+ "misc-string-literal-with-embedded-nul");
+ CheckFactories.registerCheck<SuspiciousEnumUsageCheck>(
+ "misc-suspicious-enum-usage");
+ CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
+ "misc-suspicious-missing-comma");
+ CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
+ "misc-suspicious-semicolon");
+ CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
+ "misc-suspicious-string-compare");
+ CheckFactories.registerCheck<SwappedArgumentsCheck>(
+ "misc-swapped-arguments");
+ CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
+ "misc-throw-by-value-catch-by-reference");
+ CheckFactories.registerCheck<UndelegatedConstructorCheck>(
+ "misc-undelegated-constructor");
+ CheckFactories.registerCheck<UniqueptrResetReleaseCheck>(
+ "misc-uniqueptr-reset-release");
+ CheckFactories.registerCheck<UnusedAliasDeclsCheck>(
+ "misc-unused-alias-decls");
+ CheckFactories.registerCheck<UnusedParametersCheck>(
+ "misc-unused-parameters");
+ CheckFactories.registerCheck<UnusedRAIICheck>("misc-unused-raii");
+ CheckFactories.registerCheck<UnusedUsingDeclsCheck>(
+ "misc-unused-using-decls");
+ CheckFactories.registerCheck<UseAfterMoveCheck>("misc-use-after-move");
+ CheckFactories.registerCheck<VirtualNearMissCheck>(
+ "misc-virtual-near-miss");
+ }
+};
+
+} // namespace misc
+
+// Register the MiscTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<misc::MiscModule>
+ X("misc-module", "Adds miscellaneous lint checks.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the MiscModule.
+volatile int MiscModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MisplacedConstCheck.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 "MisplacedConstCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ valueDecl(hasType(isConstQualified()),
+ hasType(typedefType(hasDeclaration(
+ typedefDecl(hasType(pointerType(unless(pointee(
+ anyOf(isConstQualified(),
+ ignoringParens(functionType())))))))
+ .bind("typedef")))))
+ .bind("decl"),
+ this);
+}
+
+static QualType guessAlternateQualification(ASTContext &Context, QualType QT) {
+ // We're given a QualType from a typedef where the qualifiers apply to the
+ // pointer instead of the pointee. Strip the const qualifier from the pointer
+ // type and add it to the pointee instead.
+ if (!QT->isPointerType())
+ return QT;
+
+ Qualifiers Quals = QT.getLocalQualifiers();
+ Quals.removeConst();
+
+ QualType NewQT = Context.getPointerType(
+ QualType(QT->getPointeeType().getTypePtr(), Qualifiers::Const));
+ return NewQT.withCVRQualifiers(Quals.getCVRQualifiers());
+}
+
+void MisplacedConstCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Var = Result.Nodes.getNodeAs<ValueDecl>("decl");
+ const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
+ ASTContext &Ctx = *Result.Context;
+ QualType CanQT = Var->getType().getCanonicalType();
+
+ diag(Var->getLocation(), "%0 declared with a const-qualified typedef type; "
+ "results in the type being '%1' instead of '%2'")
+ << Var << CanQT.getAsString(Ctx.getPrintingPolicy())
+ << guessAlternateQualification(Ctx, CanQT)
+ .getAsString(Ctx.getPrintingPolicy());
+ diag(Typedef->getLocation(), "typedef declared here", DiagnosticIDs::Note);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MisplacedConstCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check diagnoses when a const qualifier is applied to a typedef to a
+/// pointer type rather than to the pointee.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-const.html
+class MisplacedConstCheck : public ClangTidyCheck {
+public:
+ MisplacedConstCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
--- /dev/null
+//===--- MisplacedWideningCastCheck.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 "MisplacedWideningCastCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+MisplacedWideningCastCheck::MisplacedWideningCastCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
+
+void MisplacedWideningCastCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
+}
+
+void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto Calc =
+ expr(anyOf(binaryOperator(
+ anyOf(hasOperatorName("+"), hasOperatorName("-"),
+ hasOperatorName("*"), hasOperatorName("<<"))),
+ unaryOperator(hasOperatorName("~"))),
+ hasType(isInteger()))
+ .bind("Calc");
+
+ const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
+ has(ignoringParenImpCasts(Calc)));
+ const auto ImplicitCast =
+ implicitCastExpr(hasImplicitDestinationType(isInteger()),
+ has(ignoringParenImpCasts(Calc)));
+ const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
+
+ Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
+ Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
+ Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
+ Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
+ Finder->addMatcher(
+ binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
+ this);
+}
+
+static unsigned getMaxCalculationWidth(const ASTContext &Context,
+ const Expr *E) {
+ E = E->IgnoreParenImpCasts();
+
+ if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
+ unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
+ unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
+ if (Bop->getOpcode() == BO_Mul)
+ return LHSWidth + RHSWidth;
+ if (Bop->getOpcode() == BO_Add)
+ return std::max(LHSWidth, RHSWidth) + 1;
+ if (Bop->getOpcode() == BO_Rem) {
+ llvm::APSInt Val;
+ if (Bop->getRHS()->EvaluateAsInt(Val, Context))
+ return Val.getActiveBits();
+ } else if (Bop->getOpcode() == BO_Shl) {
+ llvm::APSInt Bits;
+ if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
+ // We don't handle negative values and large values well. It is assumed
+ // that compiler warnings are written for such values so the user will
+ // fix that.
+ return LHSWidth + Bits.getExtValue();
+ }
+
+ // Unknown bitcount, assume there is truncation.
+ return 1024U;
+ }
+ } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
+ // There is truncation when ~ is used.
+ if (Uop->getOpcode() == UO_Not)
+ return 1024U;
+
+ QualType T = Uop->getType();
+ return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
+ } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
+ return I->getValue().getActiveBits();
+ }
+
+ return Context.getIntWidth(E->getType());
+}
+
+static int relativeIntSizes(BuiltinType::Kind Kind) {
+ switch (Kind) {
+ case BuiltinType::UChar:
+ return 1;
+ case BuiltinType::SChar:
+ return 1;
+ case BuiltinType::Char_U:
+ return 1;
+ case BuiltinType::Char_S:
+ return 1;
+ case BuiltinType::UShort:
+ return 2;
+ case BuiltinType::Short:
+ return 2;
+ case BuiltinType::UInt:
+ return 3;
+ case BuiltinType::Int:
+ return 3;
+ case BuiltinType::ULong:
+ return 4;
+ case BuiltinType::Long:
+ return 4;
+ case BuiltinType::ULongLong:
+ return 5;
+ case BuiltinType::LongLong:
+ return 5;
+ case BuiltinType::UInt128:
+ return 6;
+ case BuiltinType::Int128:
+ return 6;
+ default:
+ return 0;
+ }
+}
+
+static int relativeCharSizes(BuiltinType::Kind Kind) {
+ switch (Kind) {
+ case BuiltinType::UChar:
+ return 1;
+ case BuiltinType::SChar:
+ return 1;
+ case BuiltinType::Char_U:
+ return 1;
+ case BuiltinType::Char_S:
+ return 1;
+ case BuiltinType::Char16:
+ return 2;
+ case BuiltinType::Char32:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static int relativeCharSizesW(BuiltinType::Kind Kind) {
+ switch (Kind) {
+ case BuiltinType::UChar:
+ return 1;
+ case BuiltinType::SChar:
+ return 1;
+ case BuiltinType::Char_U:
+ return 1;
+ case BuiltinType::Char_S:
+ return 1;
+ case BuiltinType::WChar_U:
+ return 2;
+ case BuiltinType::WChar_S:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
+ int FirstSize, SecondSize;
+ if ((FirstSize = relativeIntSizes(First)) != 0 &&
+ (SecondSize = relativeIntSizes(Second)) != 0)
+ return FirstSize > SecondSize;
+ if ((FirstSize = relativeCharSizes(First)) != 0 &&
+ (SecondSize = relativeCharSizes(Second)) != 0)
+ return FirstSize > SecondSize;
+ if ((FirstSize = relativeCharSizesW(First)) != 0 &&
+ (SecondSize = relativeCharSizesW(Second)) != 0)
+ return FirstSize > SecondSize;
+ return false;
+}
+
+void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
+ if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
+ return;
+ if (Cast->getLocStart().isMacroID())
+ return;
+
+ const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
+ if (Calc->getLocStart().isMacroID())
+ return;
+
+ ASTContext &Context = *Result.Context;
+
+ QualType CastType = Cast->getType();
+ QualType CalcType = Calc->getType();
+
+ // Explicit truncation using cast.
+ if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
+ return;
+
+ // If CalcType and CastType have same size then there is no real danger, but
+ // there can be a portability problem.
+
+ if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
+ const auto *CastBuiltinType =
+ dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
+ const auto *CalcBuiltinType =
+ dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
+ if (CastBuiltinType && CalcBuiltinType &&
+ !isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
+ return;
+ }
+
+ // Don't write a warning if we can easily see that the result is not
+ // truncated.
+ if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
+ return;
+
+ diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or "
+ "there is loss of precision before the conversion")
+ << CalcType << CastType;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MisplacedWideningCastCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find casts of calculation results to bigger type. Typically from int to
+/// long. If the intention of the cast is to avoid loss of precision then
+/// the cast is misplaced, and there can be loss of precision. Otherwise
+/// such cast is ineffective.
+///
+/// There is one option:
+///
+/// - `CheckImplicitCasts`: Whether to check implicit casts as well which may
+// be the most common case. Enabled by default.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-widening-cast.html
+class MisplacedWideningCastCheck : public ClangTidyCheck {
+public:
+ MisplacedWideningCastCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool CheckImplicitCasts;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- MoveConstantArgumentCheck.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 "MoveConstantArgumentCheck.h"
+
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ const Expr *Arg = Call->getArg(0);
+
+ CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()),
+ SM, LangOpts);
+ CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(Call->getLocEnd(),
+ Call->getLocEnd().getLocWithOffset(1)),
+ SM, LangOpts);
+
+ if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
+ Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
+ << FixItHint::CreateRemoval(AfterArgumentsRange);
+ }
+}
+
+void MoveConstantArgumentCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto MoveCallMatcher =
+ callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
+ unless(isInTemplateInstantiation()))
+ .bind("call-move");
+
+ Finder->addMatcher(MoveCallMatcher, this);
+
+ auto ConstParamMatcher = forEachArgumentWithParam(
+ MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified()))));
+
+ Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this);
+ Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"),
+ this);
+}
+
+void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+ const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
+ const Expr *Arg = CallMove->getArg(0);
+ SourceManager &SM = Result.Context->getSourceManager();
+
+ CharSourceRange MoveRange =
+ CharSourceRange::getCharRange(CallMove->getSourceRange());
+ CharSourceRange FileMoveRange =
+ Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
+ if (!FileMoveRange.isValid())
+ return;
+
+ bool IsConstArg = Arg->getType().isConstQualified();
+ bool IsTriviallyCopyable =
+ Arg->getType().isTriviallyCopyableType(*Result.Context);
+
+ if (IsConstArg || IsTriviallyCopyable) {
+ if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
+ // According to [expr.prim.lambda]p3, "whether the closure type is
+ // trivially copyable" property can be changed by the implementation of
+ // the language, so we shouldn't rely on it when issuing diagnostics.
+ if (R->isLambda())
+ return;
+ // Don't warn when the type is not copyable.
+ for (const auto *Ctor : R->ctors()) {
+ if (Ctor->isCopyConstructor() && Ctor->isDeleted())
+ return;
+ }
+ }
+ bool IsVariable = isa<DeclRefExpr>(Arg);
+ const auto *Var =
+ IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
+ auto Diag = diag(FileMoveRange.getBegin(),
+ "std::move of the %select{|const }0"
+ "%select{expression|variable %4}1 "
+ "%select{|of the trivially-copyable type %5 }2"
+ "has no effect; remove std::move()"
+ "%select{| or make the variable non-const}3")
+ << IsConstArg << IsVariable << IsTriviallyCopyable
+ << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
+ << Arg->getType();
+
+ ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
+ } else if (ReceivingExpr) {
+ auto Diag = diag(FileMoveRange.getBegin(),
+ "passing result of std::move() as a const reference "
+ "argument; no move will actually happen");
+
+ ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MoveConstantArgumentCheck.h - clang-tidy -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class MoveConstantArgumentCheck : public ClangTidyCheck {
+public:
+ MoveConstantArgumentCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
--- /dev/null
+//===--- MoveConstructorInitCheck.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 "MoveConstructorInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++11; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ unless(isImplicit()),
+ allOf(isMoveConstructor(),
+ hasAnyConstructorInitializer(
+ cxxCtorInitializer(
+ withInitializer(cxxConstructExpr(hasDeclaration(
+ cxxConstructorDecl(isCopyConstructor())
+ .bind("ctor")))))
+ .bind("move-init")))),
+ this);
+}
+
+void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+ const auto *Initializer =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
+
+ // Do not diagnose if the expression used to perform the initialization is a
+ // trivially-copyable type.
+ QualType QT = Initializer->getInit()->getType();
+ if (QT.isTriviallyCopyableType(*Result.Context))
+ return;
+
+ if (QT.isConstQualified())
+ return;
+
+ const auto *RD = QT->getAsCXXRecordDecl();
+ if (RD && RD->isTriviallyCopyable())
+ return;
+
+ // Diagnose when the class type has a move constructor available, but the
+ // ctor-initializer uses the copy constructor instead.
+ const CXXConstructorDecl *Candidate = nullptr;
+ for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
+ if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
+ !Ctor->isDeleted()) {
+ // The type has a move constructor that is at least accessible to the
+ // initializer.
+ //
+ // FIXME: Determine whether the move constructor is a viable candidate
+ // for the ctor-initializer, perhaps provide a fixit that suggests
+ // using std::move().
+ Candidate = Ctor;
+ break;
+ }
+ }
+
+ if (Candidate) {
+ // There's a move constructor candidate that the caller probably intended
+ // to call instead.
+ diag(Initializer->getSourceLocation(),
+ "move constructor initializes %0 by calling a copy constructor")
+ << (Initializer->isBaseInitializer() ? "base class" : "class member");
+ diag(CopyCtor->getLocation(), "copy constructor being called",
+ DiagnosticIDs::Note);
+ diag(Candidate->getLocation(), "candidate move constructor here",
+ DiagnosticIDs::Note);
+ }
+}
+
+void MoveConstructorInitCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MoveConstructorInitCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags user-defined move constructors that have a ctor-initializer
+/// initializing a member or base class through a copy constructor instead of a
+/// move constructor.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-move-constructor-init.html
+class MoveConstructorInitCheck : public ClangTidyCheck {
+public:
+ MoveConstructorInitCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
--- /dev/null
+//===--- MoveForwardingReferenceCheck.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 "MoveForwardingReferenceCheck.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
+ const ParmVarDecl *ParmVar,
+ const TemplateTypeParmDecl *TypeParmDecl,
+ DiagnosticBuilder &Diag,
+ const ASTContext &Context) {
+ const SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LangOpts = Context.getLangOpts();
+
+ CharSourceRange CallRange =
+ Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
+ Callee->getLocStart(), Callee->getLocEnd()),
+ SM, LangOpts);
+
+ if (CallRange.isValid()) {
+ const std::string TypeName =
+ TypeParmDecl->getIdentifier()
+ ? TypeParmDecl->getName().str()
+ : (llvm::Twine("decltype(") + ParmVar->getName() + ")").str();
+
+ const std::string ForwardName =
+ (llvm::Twine("forward<") + TypeName + ">").str();
+
+ // Create a replacement only if we see a "standard" way of calling
+ // std::move(). This will hopefully prevent erroneous replacements if the
+ // code does unusual things (e.g. create an alias for std::move() in
+ // another namespace).
+ NestedNameSpecifier *NNS = Callee->getQualifier();
+ if (!NNS) {
+ // Called as "move" (i.e. presumably the code had a "using std::move;").
+ // We still conservatively put a "std::" in front of the forward because
+ // we don't know whether the code also had a "using std::forward;".
+ Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
+ } else if (const NamespaceDecl *Namespace = NNS->getAsNamespace()) {
+ if (Namespace->getName() == "std") {
+ if (!NNS->getPrefix()) {
+ // Called as "std::move".
+ Diag << FixItHint::CreateReplacement(CallRange,
+ "std::" + ForwardName);
+ } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
+ // Called as "::std::move".
+ Diag << FixItHint::CreateReplacement(CallRange,
+ "::std::" + ForwardName);
+ }
+ }
+ }
+ }
+}
+
+void MoveForwardingReferenceCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // Matches a ParmVarDecl for a forwarding reference, i.e. a non-const rvalue
+ // reference of a function template parameter type.
+ auto ForwardingReferenceParmMatcher =
+ parmVarDecl(
+ hasType(qualType(rValueReferenceType(),
+ references(templateTypeParmType(hasDeclaration(
+ templateTypeParmDecl().bind("type-parm-decl")))),
+ unless(references(qualType(isConstQualified()))))))
+ .bind("parm-var");
+
+ Finder->addMatcher(
+ callExpr(callee(unresolvedLookupExpr(
+ hasAnyDeclaration(namedDecl(
+ hasUnderlyingDecl(hasName("::std::move")))))
+ .bind("lookup")),
+ argumentCountIs(1),
+ hasArgument(0, ignoringParenImpCasts(declRefExpr(
+ to(ForwardingReferenceParmMatcher)))))
+ .bind("call-move"),
+ this);
+}
+
+void MoveForwardingReferenceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+ const auto *UnresolvedLookup =
+ Result.Nodes.getNodeAs<UnresolvedLookupExpr>("lookup");
+ const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+ const auto *TypeParmDecl =
+ Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+ // Get the FunctionDecl and FunctionTemplateDecl containing the function
+ // parameter.
+ const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+ if (!FuncForParam)
+ return;
+ const FunctionTemplateDecl *FuncTemplate =
+ FuncForParam->getDescribedFunctionTemplate();
+ if (!FuncTemplate)
+ return;
+
+ // Check that the template type parameter belongs to the same function
+ // template as the function parameter of that type. (This implies that type
+ // deduction will happen on the type.)
+ const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+ if (!std::count(Params->begin(), Params->end(), TypeParmDecl))
+ return;
+
+ auto Diag = diag(CallMove->getExprLoc(),
+ "forwarding reference passed to std::move(), which may "
+ "unexpectedly cause lvalues to be moved; use "
+ "std::forward() instead");
+
+ replaceMoveWithForward(UnresolvedLookup, ParmVar, TypeParmDecl, Diag,
+ *Result.Context);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MoveForwardingReferenceCheck.h - clang-tidy ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check warns if std::move is applied to a forwarding reference (i.e. an
+/// rvalue reference of a function template argument type).
+///
+/// If a developer is unaware of the special rules for template argument
+/// deduction on forwarding references, it will seem reasonable to apply
+/// std::move to the forwarding reference, in the same way that this would be
+/// done for a "normal" rvalue reference.
+///
+/// This has a consequence that is usually unwanted and possibly surprising: if
+/// the function that takes the forwarding reference as its parameter is called
+/// with an lvalue, that lvalue will be moved from (and hence placed into an
+/// indeterminate state) even though no std::move was applied to the lvalue at
+/// the call site.
+//
+/// The check suggests replacing the std::move with a std::forward.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-move-forwarding-reference.html
+class MoveForwardingReferenceCheck : public ClangTidyCheck {
+public:
+ MoveForwardingReferenceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
--- /dev/null
+//===--- MultipleStatementMacroCheck.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 "MultipleStatementMacroCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER(Expr, isInMacro) { return Node.getLocStart().isMacroID(); }
+
+/// \brief Find the next statement after `S`.
+const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
+ auto Parents = Result.Context->getParents(*S);
+ if (Parents.empty())
+ return nullptr;
+ const auto *Parent = Parents[0].get<Stmt>();
+ if (!Parent)
+ return nullptr;
+ const Stmt *Prev = nullptr;
+ for (const Stmt *Child : Parent->children()) {
+ if (Prev == S)
+ return Child;
+ Prev = Child;
+ }
+ return nextStmt(Result, Parent);
+}
+
+using ExpansionRanges = std::vector<std::pair<SourceLocation, SourceLocation>>;
+
+/// \bried Get all the macro expansion ranges related to `Loc`.
+///
+/// The result is ordered from most inner to most outer.
+ExpansionRanges getExpansionRanges(SourceLocation Loc,
+ const MatchFinder::MatchResult &Result) {
+ ExpansionRanges Locs;
+ while (Loc.isMacroID()) {
+ Locs.push_back(Result.SourceManager->getImmediateExpansionRange(Loc));
+ Loc = Locs.back().first;
+ }
+ return Locs;
+}
+
+} // namespace
+
+void MultipleStatementMacroCheck::registerMatchers(MatchFinder *Finder) {
+ const auto Inner = expr(isInMacro(), unless(compoundStmt())).bind("inner");
+ Finder->addMatcher(
+ stmt(anyOf(ifStmt(hasThen(Inner)), ifStmt(hasElse(Inner)).bind("else"),
+ whileStmt(hasBody(Inner)), forStmt(hasBody(Inner))))
+ .bind("outer"),
+ this);
+}
+
+void MultipleStatementMacroCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Inner = Result.Nodes.getNodeAs<Expr>("inner");
+ const auto *Outer = Result.Nodes.getNodeAs<Stmt>("outer");
+ const auto *Next = nextStmt(Result, Outer);
+ if (!Next)
+ return;
+
+ SourceLocation OuterLoc = Outer->getLocStart();
+ if (Result.Nodes.getNodeAs<Stmt>("else"))
+ OuterLoc = cast<IfStmt>(Outer)->getElseLoc();
+
+ auto InnerRanges = getExpansionRanges(Inner->getLocStart(), Result);
+ auto OuterRanges = getExpansionRanges(OuterLoc, Result);
+ auto NextRanges = getExpansionRanges(Next->getLocStart(), Result);
+
+ // Remove all the common ranges, starting from the top (the last ones in the
+ // list).
+ while (!InnerRanges.empty() && !OuterRanges.empty() && !NextRanges.empty() &&
+ InnerRanges.back() == OuterRanges.back() &&
+ InnerRanges.back() == NextRanges.back()) {
+ InnerRanges.pop_back();
+ OuterRanges.pop_back();
+ NextRanges.pop_back();
+ }
+
+ // Inner and Next must have at least one more macro that Outer doesn't have,
+ // and that range must be common to both.
+ if (InnerRanges.empty() || NextRanges.empty() ||
+ InnerRanges.back() != NextRanges.back())
+ return;
+
+ diag(InnerRanges.back().first, "multiple statement macro used without "
+ "braces; some statements will be "
+ "unconditionally executed");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MultipleStatementMacroCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect multiple statement macros that are used in unbraced conditionals.
+/// Only the first statement of the macro will be inside the conditional and the
+/// other ones will be executed unconditionally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-multiple-statement-macro.html
+class MultipleStatementMacroCheck : public ClangTidyCheck {
+public:
+ MultipleStatementMacroCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
--- /dev/null
+//===--- NewDeleteOverloadsCheck.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 "NewDeleteOverloadsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER(FunctionDecl, isPlacementOverload) {
+ bool New;
+ switch (Node.getOverloadedOperator()) {
+ default:
+ return false;
+ case OO_New:
+ case OO_Array_New:
+ New = true;
+ break;
+ case OO_Delete:
+ case OO_Array_Delete:
+ New = false;
+ break;
+ }
+
+ // Variadic functions are always placement functions.
+ if (Node.isVariadic())
+ return true;
+
+ // Placement new is easy: it always has more than one parameter (the first
+ // parameter is always the size). If it's an overload of delete or delete[]
+ // that has only one parameter, it's never a placement delete.
+ if (New)
+ return Node.getNumParams() > 1;
+ if (Node.getNumParams() == 1)
+ return false;
+
+ // Placement delete is a little more challenging. They always have more than
+ // one parameter with the first parameter being a pointer. However, the
+ // second parameter can be a size_t for sized deallocation, and that is never
+ // a placement delete operator.
+ if (Node.getNumParams() <= 1 || Node.getNumParams() > 2)
+ return true;
+
+ const auto *FPT = Node.getType()->castAs<FunctionProtoType>();
+ ASTContext &Ctx = Node.getASTContext();
+ if (Ctx.getLangOpts().SizedDeallocation &&
+ Ctx.hasSameType(FPT->getParamType(1), Ctx.getSizeType()))
+ return false;
+
+ return true;
+}
+
+OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
+ switch (FD->getOverloadedOperator()) {
+ default:
+ break;
+ case OO_New:
+ return OO_Delete;
+ case OO_Delete:
+ return OO_New;
+ case OO_Array_New:
+ return OO_Array_Delete;
+ case OO_Array_Delete:
+ return OO_Array_New;
+ }
+ llvm_unreachable("Not an overloaded allocation operator");
+}
+
+const char *getOperatorName(OverloadedOperatorKind K) {
+ switch (K) {
+ default:
+ break;
+ case OO_New:
+ return "operator new";
+ case OO_Delete:
+ return "operator delete";
+ case OO_Array_New:
+ return "operator new[]";
+ case OO_Array_Delete:
+ return "operator delete[]";
+ }
+ llvm_unreachable("Not an overloaded allocation operator");
+}
+
+bool areCorrespondingOverloads(const FunctionDecl *LHS,
+ const FunctionDecl *RHS) {
+ return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
+}
+
+bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
+ const CXXRecordDecl *RD = nullptr) {
+ if (RD) {
+ // Check the methods in the given class and accessible to derived classes.
+ for (const auto *BMD : RD->methods())
+ if (BMD->isOverloadedOperator() && BMD->getAccess() != AS_private &&
+ areCorrespondingOverloads(MD, BMD))
+ return true;
+ } else {
+ // Get the parent class of the method; we do not need to care about checking
+ // the methods in this class as the caller has already done that by looking
+ // at the declaration contexts.
+ RD = MD->getParent();
+ }
+
+ for (const auto &BS : RD->bases()) {
+ // We can't say much about a dependent base class, but to avoid false
+ // positives assume it can have a corresponding overload.
+ if (BS.getType()->isDependentType())
+ return true;
+ if (const auto *BaseRD = BS.getType()->getAsCXXRecordDecl())
+ if (hasCorrespondingOverloadInBaseClass(MD, BaseRD))
+ return true;
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Match all operator new and operator delete overloads (including the array
+ // forms). Do not match implicit operators, placement operators, or
+ // deleted/private operators.
+ //
+ // Technically, trivially-defined operator delete seems like a reasonable
+ // thing to also skip. e.g., void operator delete(void *) {}
+ // However, I think it's more reasonable to warn in this case as the user
+ // should really be writing that as a deleted function.
+ Finder->addMatcher(
+ functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(),
+ isDeleted(), cxxMethodDecl(isPrivate()))),
+ anyOf(hasOverloadedOperatorName("new"),
+ hasOverloadedOperatorName("new[]"),
+ hasOverloadedOperatorName("delete"),
+ hasOverloadedOperatorName("delete[]")))
+ .bind("func"),
+ this);
+}
+
+void NewDeleteOverloadsCheck::check(const MatchFinder::MatchResult &Result) {
+ // Add any matches we locate to the list of things to be checked at the
+ // end of the translation unit.
+ const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const CXXRecordDecl *RD = nullptr;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+ RD = MD->getParent();
+ Overloads[RD].push_back(FD);
+}
+
+void NewDeleteOverloadsCheck::onEndOfTranslationUnit() {
+ // Walk over the list of declarations we've found to see if there is a
+ // corresponding overload at the same declaration context or within a base
+ // class. If there is not, add the element to the list of declarations to
+ // diagnose.
+ SmallVector<const FunctionDecl *, 4> Diagnose;
+ for (const auto &RP : Overloads) {
+ // We don't care about the CXXRecordDecl key in the map; we use it as a way
+ // to shard the overloads by declaration context to reduce the algorithmic
+ // complexity when searching for corresponding free store functions.
+ for (const auto *Overload : RP.second) {
+ const auto *Match =
+ std::find_if(RP.second.begin(), RP.second.end(),
+ [&Overload](const FunctionDecl *FD) {
+ if (FD == Overload)
+ return false;
+ // If the declaration contexts don't match, we don't
+ // need to check any further.
+ if (FD->getDeclContext() != Overload->getDeclContext())
+ return false;
+
+ // Since the declaration contexts match, see whether
+ // the current element is the corresponding operator.
+ if (!areCorrespondingOverloads(Overload, FD))
+ return false;
+
+ return true;
+ });
+
+ if (Match == RP.second.end()) {
+ // Check to see if there is a corresponding overload in a base class
+ // context. If there isn't, or if the overload is not a class member
+ // function, then we should diagnose.
+ const auto *MD = dyn_cast<CXXMethodDecl>(Overload);
+ if (!MD || !hasCorrespondingOverloadInBaseClass(MD))
+ Diagnose.push_back(Overload);
+ }
+ }
+ }
+
+ for (const auto *FD : Diagnose)
+ diag(FD->getLocation(), "declaration of %0 has no matching declaration "
+ "of '%1' at the same scope")
+ << FD << getOperatorName(getCorrespondingOverload(FD));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NewDeleteOverloadsCheck.h - clang-tidy----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class NewDeleteOverloadsCheck : public ClangTidyCheck {
+ std::map<const clang::CXXRecordDecl *,
+ llvm::SmallVector<const clang::FunctionDecl *, 4>>
+ Overloads;
+
+public:
+ NewDeleteOverloadsCheck(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;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
--- /dev/null
+//===--- NoexceptMoveConstructorCheck.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 "NoexceptMoveConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void NoexceptMoveConstructorCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++11; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ cxxMethodDecl(anyOf(cxxConstructorDecl(), hasOverloadedOperatorName("=")),
+ unless(isImplicit()), unless(isDeleted()))
+ .bind("decl"),
+ this);
+}
+
+void NoexceptMoveConstructorCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *Decl = Result.Nodes.getNodeAs<CXXMethodDecl>("decl")) {
+ StringRef MethodType = "assignment operator";
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Decl)) {
+ if (!Ctor->isMoveConstructor())
+ return;
+ MethodType = "constructor";
+ } else if (!Decl->isMoveAssignmentOperator()) {
+ return;
+ }
+
+ const auto *ProtoType = Decl->getType()->getAs<FunctionProtoType>();
+
+ if (isUnresolvedExceptionSpec(ProtoType->getExceptionSpecType()))
+ return;
+
+ switch (ProtoType->getNoexceptSpec(*Result.Context)) {
+ case FunctionProtoType::NR_NoNoexcept:
+ diag(Decl->getLocation(), "move %0s should be marked noexcept")
+ << MethodType;
+ // FIXME: Add a fixit.
+ break;
+ case FunctionProtoType::NR_Throw:
+ // Don't complain about nothrow(false), but complain on nothrow(expr)
+ // where expr evaluates to false.
+ if (const Expr *E = ProtoType->getNoexceptExpr()) {
+ if (isa<CXXBoolLiteralExpr>(E))
+ break;
+ diag(E->getExprLoc(),
+ "noexcept specifier on the move %0 evaluates to 'false'")
+ << MethodType;
+ }
+ break;
+ case FunctionProtoType::NR_Nothrow:
+ case FunctionProtoType::NR_Dependent:
+ case FunctionProtoType::NR_BadNoexcept:
+ break;
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NoexceptMoveConstructorCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags user-defined move constructors and assignment operators not
+/// marked with `noexcept` or marked with `noexcept(expr)` where `expr`
+/// evaluates to `false` (but is not a `false` literal itself).
+///
+/// Move constructors of all the types used with STL containers, for example,
+/// need to be declared `noexcept`. Otherwise STL will choose copy constructors
+/// instead. The same is valid for move assignment operations.
+class NoexceptMoveConstructorCheck : public ClangTidyCheck {
+public:
+ NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
--- /dev/null
+//===--- NonCopyableObjects.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 "NonCopyableObjects.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) {
+ // There are two ways to get into trouble with objects like FILE *:
+ // dereferencing the pointer type to be a non-pointer type, and declaring
+ // the type as a non-pointer type in the first place. While the declaration
+ // itself could technically be well-formed in the case where the type is not
+ // an opaque type, it's highly suspicious behavior.
+ //
+ // POSIX types are a bit different in that it's reasonable to declare a
+ // non-pointer variable or data member of the type, but it is not reasonable
+ // to dereference a pointer to the type, or declare a parameter of non-pointer
+ // type.
+ // FIXME: it would be good to make a list that is also user-configurable so
+ // that users can add their own elements to the list. However, it may require
+ // some extra thought since POSIX types and FILE types are usable in different
+ // ways.
+
+ auto BadFILEType = hasType(
+ namedDecl(hasAnyName("::FILE", "FILE", "std::FILE")).bind("type_decl"));
+ auto BadPOSIXType =
+ hasType(namedDecl(hasAnyName("::pthread_cond_t", "::pthread_mutex_t",
+ "pthread_cond_t", "pthread_mutex_t"))
+ .bind("type_decl"));
+ auto BadEitherType = anyOf(BadFILEType, BadPOSIXType);
+
+ Finder->addMatcher(
+ namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType)))
+ .bind("decl"),
+ this);
+ Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this);
+ Finder->addMatcher(
+ expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"),
+ this);
+}
+
+void NonCopyableObjectsCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *D = Result.Nodes.getNodeAs<NamedDecl>("decl");
+ const auto *BD = Result.Nodes.getNodeAs<NamedDecl>("type_decl");
+ const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+
+ if (D && BD)
+ diag(D->getLocation(), "%0 declared as type '%1', which is unsafe to copy"
+ "; did you mean '%1 *'?")
+ << D << BD->getName();
+ else if (E)
+ diag(E->getExprLoc(),
+ "expression has opaque data structure type %0; type should only be "
+ "used as a pointer and not dereferenced")
+ << BD;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
--- /dev/null
+//===--- NonCopyableObjects.h - clang-tidy-----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags dereferences and non-pointer declarations of objects that
+/// are not meant to be passed by value, such as C FILE objects.
+class NonCopyableObjectsCheck : public ClangTidyCheck {
+public:
+ NonCopyableObjectsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
--- /dev/null
+//===--- RedundantExpressionCheck.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 "RedundantExpressionCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+using llvm::APSInt;
+} // namespace
+
+static const char KnownBannedMacroNames[] =
+ "EAGAIN;EWOULDBLOCK;SIGCLD;SIGCHLD;";
+
+static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) {
+ Result = Value;
+ ++Result;
+ return Value < Result;
+}
+
+static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left,
+ const NestedNameSpecifier *Right) {
+ llvm::FoldingSetNodeID LeftID, RightID;
+ Left->Profile(LeftID);
+ Right->Profile(RightID);
+ return LeftID == RightID;
+}
+
+static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
+ if (!Left || !Right)
+ return !Left && !Right;
+
+ Left = Left->IgnoreParens();
+ Right = Right->IgnoreParens();
+
+ // Compare classes.
+ if (Left->getStmtClass() != Right->getStmtClass())
+ return false;
+
+ // Compare children.
+ Expr::const_child_iterator LeftIter = Left->child_begin();
+ Expr::const_child_iterator RightIter = Right->child_begin();
+ while (LeftIter != Left->child_end() && RightIter != Right->child_end()) {
+ if (!areEquivalentExpr(dyn_cast<Expr>(*LeftIter),
+ dyn_cast<Expr>(*RightIter)))
+ return false;
+ ++LeftIter;
+ ++RightIter;
+ }
+ if (LeftIter != Left->child_end() || RightIter != Right->child_end())
+ return false;
+
+ // Perform extra checks.
+ switch (Left->getStmtClass()) {
+ default:
+ return false;
+
+ case Stmt::CharacterLiteralClass:
+ return cast<CharacterLiteral>(Left)->getValue() ==
+ cast<CharacterLiteral>(Right)->getValue();
+ case Stmt::IntegerLiteralClass: {
+ llvm::APInt LeftLit = cast<IntegerLiteral>(Left)->getValue();
+ llvm::APInt RightLit = cast<IntegerLiteral>(Right)->getValue();
+ return LeftLit.getBitWidth() == RightLit.getBitWidth() &&
+ LeftLit == RightLit;
+ }
+ case Stmt::FloatingLiteralClass:
+ return cast<FloatingLiteral>(Left)->getValue().bitwiseIsEqual(
+ cast<FloatingLiteral>(Right)->getValue());
+ case Stmt::StringLiteralClass:
+ return cast<StringLiteral>(Left)->getBytes() ==
+ cast<StringLiteral>(Right)->getBytes();
+
+ case Stmt::DependentScopeDeclRefExprClass:
+ if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() !=
+ cast<DependentScopeDeclRefExpr>(Right)->getDeclName())
+ return false;
+ return areEquivalentNameSpecifier(
+ cast<DependentScopeDeclRefExpr>(Left)->getQualifier(),
+ cast<DependentScopeDeclRefExpr>(Right)->getQualifier());
+ case Stmt::DeclRefExprClass:
+ return cast<DeclRefExpr>(Left)->getDecl() ==
+ cast<DeclRefExpr>(Right)->getDecl();
+ case Stmt::MemberExprClass:
+ return cast<MemberExpr>(Left)->getMemberDecl() ==
+ cast<MemberExpr>(Right)->getMemberDecl();
+
+ case Stmt::CStyleCastExprClass:
+ return cast<CStyleCastExpr>(Left)->getTypeAsWritten() ==
+ cast<CStyleCastExpr>(Right)->getTypeAsWritten();
+
+ case Stmt::CallExprClass:
+ case Stmt::ImplicitCastExprClass:
+ case Stmt::ArraySubscriptExprClass:
+ return true;
+
+ case Stmt::UnaryOperatorClass:
+ if (cast<UnaryOperator>(Left)->isIncrementDecrementOp())
+ return false;
+ return cast<UnaryOperator>(Left)->getOpcode() ==
+ cast<UnaryOperator>(Right)->getOpcode();
+ case Stmt::BinaryOperatorClass:
+ return cast<BinaryOperator>(Left)->getOpcode() ==
+ cast<BinaryOperator>(Right)->getOpcode();
+ }
+}
+
+// For a given expression 'x', returns whether the ranges covered by the
+// relational operators are equivalent (i.e. x <= 4 is equivalent to x < 5).
+static bool areEquivalentRanges(BinaryOperatorKind OpcodeLHS,
+ const APSInt &ValueLHS,
+ BinaryOperatorKind OpcodeRHS,
+ const APSInt &ValueRHS) {
+ assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+ "Values must be ordered");
+ // Handle the case where constants are the same: x <= 4 <==> x <= 4.
+ if (APSInt::compareValues(ValueLHS, ValueRHS) == 0)
+ return OpcodeLHS == OpcodeRHS;
+
+ // Handle the case where constants are off by one: x <= 4 <==> x < 5.
+ APSInt ValueLHS_plus1;
+ return ((OpcodeLHS == BO_LE && OpcodeRHS == BO_LT) ||
+ (OpcodeLHS == BO_GT && OpcodeRHS == BO_GE)) &&
+ incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+ APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0;
+}
+
+// For a given expression 'x', returns whether the ranges covered by the
+// relational operators are fully disjoint (i.e. x < 4 and x > 7).
+static bool areExclusiveRanges(BinaryOperatorKind OpcodeLHS,
+ const APSInt &ValueLHS,
+ BinaryOperatorKind OpcodeRHS,
+ const APSInt &ValueRHS) {
+ assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+ "Values must be ordered");
+
+ // Handle cases where the constants are the same.
+ if (APSInt::compareValues(ValueLHS, ValueRHS) == 0) {
+ switch (OpcodeLHS) {
+ case BO_EQ:
+ return OpcodeRHS == BO_NE || OpcodeRHS == BO_GT || OpcodeRHS == BO_LT;
+ case BO_NE:
+ return OpcodeRHS == BO_EQ;
+ case BO_LE:
+ return OpcodeRHS == BO_GT;
+ case BO_GE:
+ return OpcodeRHS == BO_LT;
+ case BO_LT:
+ return OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE;
+ case BO_GT:
+ return OpcodeRHS == BO_EQ || OpcodeRHS == BO_LT || OpcodeRHS == BO_LE;
+ default:
+ return false;
+ }
+ }
+
+ // Handle cases where the constants are different.
+ if ((OpcodeLHS == BO_EQ || OpcodeLHS == BO_LT || OpcodeLHS == BO_LE) &&
+ (OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE))
+ return true;
+
+ // Handle the case where constants are off by one: x > 5 && x < 6.
+ APSInt ValueLHS_plus1;
+ if (OpcodeLHS == BO_GT && OpcodeRHS == BO_LT &&
+ incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+ APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0)
+ return true;
+
+ return false;
+}
+
+// Returns whether the ranges covered by the union of both relational
+// expressions covers the whole domain (i.e. x < 10 and x > 0).
+static bool rangesFullyCoverDomain(BinaryOperatorKind OpcodeLHS,
+ const APSInt &ValueLHS,
+ BinaryOperatorKind OpcodeRHS,
+ const APSInt &ValueRHS) {
+ assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+ "Values must be ordered");
+
+ // Handle cases where the constants are the same: x < 5 || x >= 5.
+ if (APSInt::compareValues(ValueLHS, ValueRHS) == 0) {
+ switch (OpcodeLHS) {
+ case BO_EQ:
+ return OpcodeRHS == BO_NE;
+ case BO_NE:
+ return OpcodeRHS == BO_EQ;
+ case BO_LE:
+ return OpcodeRHS == BO_GT || OpcodeRHS == BO_GE;
+ case BO_LT:
+ return OpcodeRHS == BO_GE;
+ case BO_GE:
+ return OpcodeRHS == BO_LT || OpcodeRHS == BO_LE;
+ case BO_GT:
+ return OpcodeRHS == BO_LE;
+ default:
+ return false;
+ }
+ }
+
+ // Handle the case where constants are off by one: x <= 4 || x >= 5.
+ APSInt ValueLHS_plus1;
+ if (OpcodeLHS == BO_LE && OpcodeRHS == BO_GE &&
+ incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+ APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0)
+ return true;
+
+ // Handle cases where the constants are different: x > 4 || x <= 7.
+ if ((OpcodeLHS == BO_GT || OpcodeLHS == BO_GE) &&
+ (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE))
+ return true;
+
+ // Handle cases where constants are different but both ops are !=, like:
+ // x != 5 || x != 10
+ if (OpcodeLHS == BO_NE && OpcodeRHS == BO_NE)
+ return true;
+
+ return false;
+}
+
+static bool rangeSubsumesRange(BinaryOperatorKind OpcodeLHS,
+ const APSInt &ValueLHS,
+ BinaryOperatorKind OpcodeRHS,
+ const APSInt &ValueRHS) {
+ int Comparison = APSInt::compareValues(ValueLHS, ValueRHS);
+ switch (OpcodeLHS) {
+ case BO_EQ:
+ return OpcodeRHS == BO_EQ && Comparison == 0;
+ case BO_NE:
+ return (OpcodeRHS == BO_NE && Comparison == 0) ||
+ (OpcodeRHS == BO_EQ && Comparison != 0) ||
+ (OpcodeRHS == BO_LT && Comparison >= 0) ||
+ (OpcodeRHS == BO_LE && Comparison > 0) ||
+ (OpcodeRHS == BO_GT && Comparison <= 0) ||
+ (OpcodeRHS == BO_GE && Comparison < 0);
+
+ case BO_LT:
+ return ((OpcodeRHS == BO_LT && Comparison >= 0) ||
+ (OpcodeRHS == BO_LE && Comparison > 0) ||
+ (OpcodeRHS == BO_EQ && Comparison > 0));
+ case BO_GT:
+ return ((OpcodeRHS == BO_GT && Comparison <= 0) ||
+ (OpcodeRHS == BO_GE && Comparison < 0) ||
+ (OpcodeRHS == BO_EQ && Comparison < 0));
+ case BO_LE:
+ return (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE || OpcodeRHS == BO_EQ) &&
+ Comparison >= 0;
+ case BO_GE:
+ return (OpcodeRHS == BO_GT || OpcodeRHS == BO_GE || OpcodeRHS == BO_EQ) &&
+ Comparison <= 0;
+ default:
+ return false;
+ }
+}
+
+static void canonicalNegateExpr(BinaryOperatorKind &Opcode, APSInt &Value) {
+ if (Opcode == BO_Sub) {
+ Opcode = BO_Add;
+ Value = -Value;
+ }
+}
+
+AST_MATCHER(Expr, isIntegerConstantExpr) {
+ if (Node.isInstantiationDependent())
+ return false;
+ return Node.isIntegerConstantExpr(Finder->getASTContext());
+}
+
+// Returns a matcher for integer constant expression.
+static ast_matchers::internal::Matcher<Expr>
+matchIntegerConstantExpr(StringRef Id) {
+ std::string CstId = (Id + "-const").str();
+ return expr(isIntegerConstantExpr()).bind(CstId);
+}
+
+// Retrieve the integer value matched by 'matchIntegerConstantExpr' with name
+// 'Id' and store it into 'Value'.
+static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+ StringRef Id, APSInt &Value) {
+ std::string CstId = (Id + "-const").str();
+ const auto *CstExpr = Result.Nodes.getNodeAs<Expr>(CstId);
+ return CstExpr && CstExpr->isIntegerConstantExpr(Value, *Result.Context);
+}
+
+// Returns a matcher for a symbolic expression (any expression except ingeter
+// constant expression).
+static ast_matchers::internal::Matcher<Expr> matchSymbolicExpr(StringRef Id) {
+ std::string SymId = (Id + "-sym").str();
+ return ignoringParenImpCasts(
+ expr(unless(isIntegerConstantExpr())).bind(SymId));
+}
+
+// Retrieve the expression matched by 'matchSymbolicExpr' with name 'Id' and
+// store it into 'SymExpr'.
+static bool retrieveSymbolicExpr(const MatchFinder::MatchResult &Result,
+ StringRef Id, const Expr *&SymExpr) {
+ std::string SymId = (Id + "-sym").str();
+ if (const auto *Node = Result.Nodes.getNodeAs<Expr>(SymId)) {
+ SymExpr = Node;
+ return true;
+ }
+ return false;
+}
+
+// Match a binary operator between a symbolic expression and an integer constant
+// expression.
+static ast_matchers::internal::Matcher<Expr>
+matchBinOpIntegerConstantExpr(StringRef Id) {
+ const auto BinOpCstExpr =
+ expr(
+ anyOf(binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|"),
+ hasOperatorName("&")),
+ hasEitherOperand(matchSymbolicExpr(Id)),
+ hasEitherOperand(matchIntegerConstantExpr(Id))),
+ binaryOperator(hasOperatorName("-"),
+ hasLHS(matchSymbolicExpr(Id)),
+ hasRHS(matchIntegerConstantExpr(Id)))))
+ .bind(Id);
+ return ignoringParenImpCasts(BinOpCstExpr);
+}
+
+// Retrieve sub-expressions matched by 'matchBinOpIntegerConstantExpr' with
+// name 'Id'.
+static bool
+retrieveBinOpIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+ StringRef Id, BinaryOperatorKind &Opcode,
+ const Expr *&Symbol, APSInt &Value) {
+ if (const auto *BinExpr = Result.Nodes.getNodeAs<BinaryOperator>(Id)) {
+ Opcode = BinExpr->getOpcode();
+ return retrieveSymbolicExpr(Result, Id, Symbol) &&
+ retrieveIntegerConstantExpr(Result, Id, Value);
+ }
+ return false;
+}
+
+// Matches relational expression: 'Expr <op> k' (i.e. x < 2, x != 3, 12 <= x).
+static ast_matchers::internal::Matcher<Expr>
+matchRelationalIntegerConstantExpr(StringRef Id) {
+ std::string CastId = (Id + "-cast").str();
+ std::string SwapId = (Id + "-swap").str();
+ std::string NegateId = (Id + "-negate").str();
+
+ const auto RelationalExpr = ignoringParenImpCasts(binaryOperator(
+ isComparisonOperator(), expr().bind(Id),
+ anyOf(allOf(hasLHS(matchSymbolicExpr(Id)),
+ hasRHS(matchIntegerConstantExpr(Id))),
+ allOf(hasLHS(matchIntegerConstantExpr(Id)),
+ hasRHS(matchSymbolicExpr(Id)), expr().bind(SwapId)))));
+
+ // A cast can be matched as a comparator to zero. (i.e. if (x) is equivalent
+ // to if (x != 0)).
+ const auto CastExpr =
+ implicitCastExpr(hasCastKind(CK_IntegralToBoolean),
+ hasSourceExpression(matchSymbolicExpr(Id)))
+ .bind(CastId);
+
+ const auto NegateRelationalExpr =
+ unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(anyOf(CastExpr, RelationalExpr)))
+ .bind(NegateId);
+
+ const auto NegateNegateRelationalExpr =
+ unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(unaryOperator(
+ hasOperatorName("!"),
+ hasUnaryOperand(anyOf(CastExpr, RelationalExpr)))));
+
+ return anyOf(RelationalExpr, CastExpr, NegateRelationalExpr,
+ NegateNegateRelationalExpr);
+}
+
+// Retrieve sub-expressions matched by 'matchRelationalIntegerConstantExpr' with
+// name 'Id'.
+static bool
+retrieveRelationalIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+ StringRef Id, const Expr *&OperandExpr,
+ BinaryOperatorKind &Opcode,
+ const Expr *&Symbol, APSInt &Value) {
+ std::string CastId = (Id + "-cast").str();
+ std::string SwapId = (Id + "-swap").str();
+ std::string NegateId = (Id + "-negate").str();
+
+ if (const auto *Bin = Result.Nodes.getNodeAs<BinaryOperator>(Id)) {
+ // Operand received with explicit comparator.
+ Opcode = Bin->getOpcode();
+ OperandExpr = Bin;
+ if (!retrieveIntegerConstantExpr(Result, Id, Value))
+ return false;
+ } else if (const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(CastId)) {
+ // Operand received with implicit comparator (cast).
+ Opcode = BO_NE;
+ OperandExpr = Cast;
+ Value = APSInt(32, false);
+ } else {
+ return false;
+ }
+
+ if (!retrieveSymbolicExpr(Result, Id, Symbol))
+ return false;
+
+ if (Result.Nodes.getNodeAs<Expr>(SwapId))
+ Opcode = BinaryOperator::reverseComparisonOp(Opcode);
+ if (Result.Nodes.getNodeAs<Expr>(NegateId))
+ Opcode = BinaryOperator::negateComparisonOp(Opcode);
+
+ return true;
+}
+
+AST_MATCHER(BinaryOperator, operandsAreEquivalent) {
+ return areEquivalentExpr(Node.getLHS(), Node.getRHS());
+}
+
+AST_MATCHER(ConditionalOperator, expressionsAreEquivalent) {
+ return areEquivalentExpr(Node.getTrueExpr(), Node.getFalseExpr());
+}
+
+AST_MATCHER(CallExpr, parametersAreEquivalent) {
+ return Node.getNumArgs() == 2 &&
+ areEquivalentExpr(Node.getArg(0), Node.getArg(1));
+}
+
+AST_MATCHER(BinaryOperator, binaryOperatorIsInMacro) {
+ return Node.getOperatorLoc().isMacroID();
+}
+
+AST_MATCHER(ConditionalOperator, conditionalOperatorIsInMacro) {
+ return Node.getQuestionLoc().isMacroID() || Node.getColonLoc().isMacroID();
+}
+
+AST_MATCHER(Expr, isMacro) { return Node.getExprLoc().isMacroID(); }
+
+AST_MATCHER_P(Expr, expandedByMacro, std::set<std::string>, Names) {
+ const SourceManager &SM = Finder->getASTContext().getSourceManager();
+ const LangOptions &LO = Finder->getASTContext().getLangOpts();
+ SourceLocation Loc = Node.getExprLoc();
+ while (Loc.isMacroID()) {
+ std::string MacroName = Lexer::getImmediateMacroName(Loc, SM, LO);
+ if (Names.find(MacroName) != Names.end())
+ return true;
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+ return false;
+}
+
+void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
+ const auto AnyLiteralExpr = ignoringParenImpCasts(
+ anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral()));
+
+ std::vector<std::string> MacroNames =
+ utils::options::parseStringList(KnownBannedMacroNames);
+ std::set<std::string> Names(MacroNames.begin(), MacroNames.end());
+
+ const auto BannedIntegerLiteral = integerLiteral(expandedByMacro(Names));
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("-"), hasOperatorName("/"),
+ hasOperatorName("%"), hasOperatorName("|"),
+ hasOperatorName("&"), hasOperatorName("^"),
+ matchers::isComparisonOperator(),
+ hasOperatorName("&&"), hasOperatorName("||"),
+ hasOperatorName("=")),
+ operandsAreEquivalent(),
+ // Filter noisy false positives.
+ unless(isInTemplateInstantiation()),
+ unless(binaryOperatorIsInMacro()),
+ unless(hasType(realFloatingPointType())),
+ unless(hasEitherOperand(hasType(realFloatingPointType()))),
+ unless(hasLHS(AnyLiteralExpr)),
+ unless(hasDescendant(BannedIntegerLiteral)))
+ .bind("binary"),
+ this);
+
+ Finder->addMatcher(
+ conditionalOperator(expressionsAreEquivalent(),
+ // Filter noisy false positives.
+ unless(conditionalOperatorIsInMacro()),
+ unless(hasTrueExpression(AnyLiteralExpr)),
+ unless(isInTemplateInstantiation()))
+ .bind("cond"),
+ this);
+
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ anyOf(
+ hasOverloadedOperatorName("-"), hasOverloadedOperatorName("/"),
+ hasOverloadedOperatorName("%"), hasOverloadedOperatorName("|"),
+ hasOverloadedOperatorName("&"), hasOverloadedOperatorName("^"),
+ hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!="),
+ hasOverloadedOperatorName("<"), hasOverloadedOperatorName("<="),
+ hasOverloadedOperatorName(">"), hasOverloadedOperatorName(">="),
+ hasOverloadedOperatorName("&&"), hasOverloadedOperatorName("||"),
+ hasOverloadedOperatorName("=")),
+ parametersAreEquivalent(),
+ // Filter noisy false positives.
+ unless(isMacro()), unless(isInTemplateInstantiation()))
+ .bind("call"),
+ this);
+
+ // Match common expressions and apply more checks to find redundant
+ // sub-expressions.
+ // a) Expr <op> K1 == K2
+ // b) Expr <op> K1 == Expr
+ // c) Expr <op> K1 == Expr <op> K2
+ // see: 'checkArithmeticExpr' and 'checkBitwiseExpr'
+ const auto BinOpCstLeft = matchBinOpIntegerConstantExpr("lhs");
+ const auto BinOpCstRight = matchBinOpIntegerConstantExpr("rhs");
+ const auto CstRight = matchIntegerConstantExpr("rhs");
+ const auto SymRight = matchSymbolicExpr("rhs");
+
+ // Match expressions like: x <op> 0xFF == 0xF00.
+ Finder->addMatcher(binaryOperator(isComparisonOperator(),
+ hasEitherOperand(BinOpCstLeft),
+ hasEitherOperand(CstRight))
+ .bind("binop-const-compare-to-const"),
+ this);
+
+ // Match expressions like: x <op> 0xFF == x.
+ Finder->addMatcher(
+ binaryOperator(isComparisonOperator(),
+ anyOf(allOf(hasLHS(BinOpCstLeft), hasRHS(SymRight)),
+ allOf(hasLHS(SymRight), hasRHS(BinOpCstLeft))))
+ .bind("binop-const-compare-to-sym"),
+ this);
+
+ // Match expressions like: x <op> 10 == x <op> 12.
+ Finder->addMatcher(binaryOperator(isComparisonOperator(),
+ hasLHS(BinOpCstLeft), hasRHS(BinOpCstRight),
+ // Already reported as redundant.
+ unless(operandsAreEquivalent()))
+ .bind("binop-const-compare-to-binop-const"),
+ this);
+
+ // Match relational expressions combined with logical operators and find
+ // redundant sub-expressions.
+ // see: 'checkRelationalExpr'
+
+ // Match expressions like: x < 2 && x > 2.
+ const auto ComparisonLeft = matchRelationalIntegerConstantExpr("lhs");
+ const auto ComparisonRight = matchRelationalIntegerConstantExpr("rhs");
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("||"), hasOperatorName("&&")),
+ hasLHS(ComparisonLeft), hasRHS(ComparisonRight),
+ // Already reported as redundant.
+ unless(operandsAreEquivalent()))
+ .bind("comparisons-of-symbol-and-const"),
+ this);
+}
+
+void RedundantExpressionCheck::checkArithmeticExpr(
+ const MatchFinder::MatchResult &Result) {
+ APSInt LhsValue, RhsValue;
+ const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr;
+ BinaryOperatorKind LhsOpcode, RhsOpcode;
+
+ if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+ "binop-const-compare-to-sym")) {
+ BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+ if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+ LhsValue) ||
+ !retrieveSymbolicExpr(Result, "rhs", RhsSymbol) ||
+ !areEquivalentExpr(LhsSymbol, RhsSymbol))
+ return;
+
+ // Check expressions: x + k == x or x - k == x.
+ if (LhsOpcode == BO_Add || LhsOpcode == BO_Sub) {
+ if ((LhsValue != 0 && Opcode == BO_EQ) ||
+ (LhsValue == 0 && Opcode == BO_NE))
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always false");
+ else if ((LhsValue == 0 && Opcode == BO_EQ) ||
+ (LhsValue != 0 && Opcode == BO_NE))
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always true");
+ }
+ } else if (const auto *ComparisonOperator =
+ Result.Nodes.getNodeAs<BinaryOperator>(
+ "binop-const-compare-to-binop-const")) {
+ BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+ if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+ LhsValue) ||
+ !retrieveBinOpIntegerConstantExpr(Result, "rhs", RhsOpcode, RhsSymbol,
+ RhsValue) ||
+ !areEquivalentExpr(LhsSymbol, RhsSymbol))
+ return;
+
+ canonicalNegateExpr(LhsOpcode, LhsValue);
+ canonicalNegateExpr(RhsOpcode, RhsValue);
+
+ // Check expressions: x + 1 == x + 2 or x + 1 != x + 2.
+ if (LhsOpcode == BO_Add && RhsOpcode == BO_Add) {
+ if ((Opcode == BO_EQ && APSInt::compareValues(LhsValue, RhsValue) == 0) ||
+ (Opcode == BO_NE && APSInt::compareValues(LhsValue, RhsValue) != 0)) {
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always true");
+ } else if ((Opcode == BO_EQ &&
+ APSInt::compareValues(LhsValue, RhsValue) != 0) ||
+ (Opcode == BO_NE &&
+ APSInt::compareValues(LhsValue, RhsValue) == 0)) {
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always false");
+ }
+ }
+ }
+}
+
+void RedundantExpressionCheck::checkBitwiseExpr(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+ "binop-const-compare-to-const")) {
+ BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+ APSInt LhsValue, RhsValue;
+ const Expr *LhsSymbol = nullptr;
+ BinaryOperatorKind LhsOpcode;
+ if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+ LhsValue) ||
+ !retrieveIntegerConstantExpr(Result, "rhs", RhsValue))
+ return;
+
+ uint64_t LhsConstant = LhsValue.getZExtValue();
+ uint64_t RhsConstant = RhsValue.getZExtValue();
+ SourceLocation Loc = ComparisonOperator->getOperatorLoc();
+
+ // Check expression: x & k1 == k2 (i.e. x & 0xFF == 0xF00)
+ if (LhsOpcode == BO_And && (LhsConstant & RhsConstant) != RhsConstant) {
+ if (Opcode == BO_EQ)
+ diag(Loc, "logical expression is always false");
+ else if (Opcode == BO_NE)
+ diag(Loc, "logical expression is always true");
+ }
+
+ // Check expression: x | k1 == k2 (i.e. x | 0xFF == 0xF00)
+ if (LhsOpcode == BO_Or && (LhsConstant | RhsConstant) != RhsConstant) {
+ if (Opcode == BO_EQ)
+ diag(Loc, "logical expression is always false");
+ else if (Opcode == BO_NE)
+ diag(Loc, "logical expression is always true");
+ }
+ }
+}
+
+void RedundantExpressionCheck::checkRelationalExpr(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+ "comparisons-of-symbol-and-const")) {
+ // Matched expressions are: (x <op> k1) <REL> (x <op> k2).
+ BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+ const Expr *LhsExpr = nullptr, *RhsExpr = nullptr;
+ APSInt LhsValue, RhsValue;
+ const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr;
+ BinaryOperatorKind LhsOpcode, RhsOpcode;
+ if (!retrieveRelationalIntegerConstantExpr(
+ Result, "lhs", LhsExpr, LhsOpcode, LhsSymbol, LhsValue) ||
+ !retrieveRelationalIntegerConstantExpr(
+ Result, "rhs", RhsExpr, RhsOpcode, RhsSymbol, RhsValue) ||
+ !areEquivalentExpr(LhsSymbol, RhsSymbol))
+ return;
+
+ // Bring to a canonical form: smallest constant must be on the left side.
+ if (APSInt::compareValues(LhsValue, RhsValue) > 0) {
+ std::swap(LhsExpr, RhsExpr);
+ std::swap(LhsValue, RhsValue);
+ std::swap(LhsSymbol, RhsSymbol);
+ std::swap(LhsOpcode, RhsOpcode);
+ }
+
+ if ((Opcode == BO_LAnd || Opcode == BO_LOr) &&
+ areEquivalentRanges(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+ diag(ComparisonOperator->getOperatorLoc(),
+ "equivalent expression on both side of logical operator");
+ return;
+ }
+
+ if (Opcode == BO_LAnd) {
+ if (areExclusiveRanges(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always false");
+ } else if (rangeSubsumesRange(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+ diag(LhsExpr->getExprLoc(), "expression is redundant");
+ } else if (rangeSubsumesRange(RhsOpcode, RhsValue, LhsOpcode, LhsValue)) {
+ diag(RhsExpr->getExprLoc(), "expression is redundant");
+ }
+ }
+
+ if (Opcode == BO_LOr) {
+ if (rangesFullyCoverDomain(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+ diag(ComparisonOperator->getOperatorLoc(),
+ "logical expression is always true");
+ } else if (rangeSubsumesRange(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+ diag(RhsExpr->getExprLoc(), "expression is redundant");
+ } else if (rangeSubsumesRange(RhsOpcode, RhsValue, LhsOpcode, LhsValue)) {
+ diag(LhsExpr->getExprLoc(), "expression is redundant");
+ }
+ }
+ }
+}
+
+void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary"))
+ diag(BinOp->getOperatorLoc(), "both side of operator are equivalent");
+ if (const auto *CondOp = Result.Nodes.getNodeAs<ConditionalOperator>("cond"))
+ diag(CondOp->getColonLoc(), "'true' and 'false' expression are equivalent");
+ if (const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("call"))
+ diag(Call->getOperatorLoc(),
+ "both side of overloaded operator are equivalent");
+
+ checkArithmeticExpr(Result);
+ checkBitwiseExpr(Result);
+ checkRelationalExpr(Result);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantExpressionCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect useless or suspicious redundant expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-redundant-expression.html
+class RedundantExpressionCheck : public ClangTidyCheck {
+public:
+ RedundantExpressionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void checkArithmeticExpr(const ast_matchers::MatchFinder::MatchResult &R);
+ void checkBitwiseExpr(const ast_matchers::MatchFinder::MatchResult &R);
+ void checkRelationalExpr(const ast_matchers::MatchFinder::MatchResult &R);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
--- /dev/null
+//===--- SizeofContainerCheck.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 "SizeofContainerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ expr(unless(isInTemplateInstantiation()),
+ expr(sizeOfExpr(has(ignoringParenImpCasts(
+ expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
+ matchesName("^(::std::|::string)"),
+ unless(matchesName("^::std::(bitset|array)$")),
+ hasMethod(cxxMethodDecl(hasName("size"), isPublic(),
+ isConst())))))))))))
+ .bind("sizeof"),
+ // Ignore ARRAYSIZE(<array of containers>) pattern.
+ unless(hasAncestor(binaryOperator(
+ anyOf(hasOperatorName("/"), hasOperatorName("%")),
+ hasLHS(ignoringParenCasts(sizeOfExpr(expr()))),
+ hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))),
+ this);
+}
+
+void SizeofContainerCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SizeOf =
+ Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof");
+
+ auto Diag =
+ diag(SizeOf->getLocStart(), "sizeof() doesn't return the size of the "
+ "container; did you mean .size()?");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SizeofContainerCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find usages of sizeof on expressions of STL container types. Most likely the
+/// user wanted to use `.size()` instead.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-sizeof-container.html
+class SizeofContainerCheck : public ClangTidyCheck {
+public:
+ SizeofContainerCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
--- /dev/null
+//===--- SizeofExpressionCheck.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 "SizeofExpressionCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
+ return Node.getValue().getZExtValue() > N;
+}
+
+AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ if (Depth < 0)
+ return false;
+
+ const Expr *E = Node.IgnoreParenImpCasts();
+ if (InnerMatcher.matches(*E, Finder, Builder))
+ return true;
+
+ if (const auto *CE = dyn_cast<CastExpr>(E)) {
+ const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+ return M.matches(*CE->getSubExpr(), Finder, Builder);
+ } else if (const auto *UE = dyn_cast<UnaryOperator>(E)) {
+ const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+ return M.matches(*UE->getSubExpr(), Finder, Builder);
+ } else if (const auto *BE = dyn_cast<BinaryOperator>(E)) {
+ const auto LHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+ const auto RHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+ return LHS.matches(*BE->getLHS(), Finder, Builder) ||
+ RHS.matches(*BE->getRHS(), Finder, Builder);
+ }
+
+ return false;
+}
+
+CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) {
+ if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
+ isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
+ return CharUnits::Zero();
+ return Ctx.getTypeSizeInChars(Ty);
+}
+
+} // namespace
+
+SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnSizeOfConstant(Options.get("WarnOnSizeOfConstant", 1) != 0),
+ WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", 1) != 0),
+ WarnOnSizeOfCompareToConstant(
+ Options.get("WarnOnSizeOfCompareToConstant", 1) != 0) {}
+
+void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
+ Options.store(Opts, "WarnOnSizeOfThis", WarnOnSizeOfThis);
+ Options.store(Opts, "WarnOnSizeOfCompareToConstant",
+ WarnOnSizeOfCompareToConstant);
+}
+
+void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
+ const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
+ const auto ConstantExpr = expr(ignoringParenImpCasts(
+ anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
+ binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr)))));
+ const auto SizeOfExpr =
+ expr(anyOf(sizeOfExpr(has(type())), sizeOfExpr(has(expr()))));
+ const auto SizeOfZero = expr(
+ sizeOfExpr(has(ignoringParenImpCasts(expr(integerLiteral(equals(0)))))));
+
+ // Detect expression like: sizeof(ARRAYLEN);
+ // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know
+ // the sizeof size_t.
+ if (WarnOnSizeOfConstant) {
+ Finder->addMatcher(
+ expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
+ unless(SizeOfZero))
+ .bind("sizeof-constant"),
+ this);
+ }
+
+ // Detect expression like: sizeof(this);
+ if (WarnOnSizeOfThis) {
+ Finder->addMatcher(
+ expr(sizeOfExpr(has(ignoringParenImpCasts(expr(cxxThisExpr())))))
+ .bind("sizeof-this"),
+ this);
+ }
+
+ // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
+ const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
+ const auto ConstStrLiteralDecl =
+ varDecl(isDefinition(), hasType(qualType(hasCanonicalType(CharPtrType))),
+ hasInitializer(ignoringParenImpCasts(stringLiteral())));
+ Finder->addMatcher(expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
+ hasType(qualType(hasCanonicalType(CharPtrType))),
+ ignoringParenImpCasts(declRefExpr(
+ hasDeclaration(ConstStrLiteralDecl))))))))
+ .bind("sizeof-charp"),
+ this);
+
+ // Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
+ const auto ArrayExpr = expr(ignoringParenImpCasts(
+ expr(hasType(qualType(hasCanonicalType(arrayType()))))));
+ const auto ArrayCastExpr = expr(anyOf(
+ unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))),
+ binaryOperator(hasEitherOperand(ArrayExpr)),
+ castExpr(hasSourceExpression(ArrayExpr))));
+ const auto PointerToArrayExpr = expr(ignoringParenImpCasts(expr(
+ hasType(qualType(hasCanonicalType(pointerType(pointee(arrayType()))))))));
+
+ const auto StructAddrOfExpr =
+ unaryOperator(hasOperatorName("&"),
+ hasUnaryOperand(ignoringParenImpCasts(expr(
+ hasType(qualType(hasCanonicalType(recordType())))))));
+
+ Finder->addMatcher(
+ expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
+ anyOf(ArrayCastExpr, PointerToArrayExpr, StructAddrOfExpr))))))
+ .bind("sizeof-pointer-to-aggregate"),
+ this);
+
+ // Detect expression like: sizeof(epxr) <= k for a suspicious constant 'k'.
+ if (WarnOnSizeOfCompareToConstant) {
+ Finder->addMatcher(
+ binaryOperator(matchers::isRelationalOperator(),
+ hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
+ hasEitherOperand(ignoringParenImpCasts(
+ anyOf(integerLiteral(equals(0)),
+ integerLiteral(isBiggerThan(0x80000))))))
+ .bind("sizeof-compare-constant"),
+ this);
+ }
+
+ // Detect expression like: sizeof(expr, expr); most likely an error.
+ Finder->addMatcher(expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
+ binaryOperator(hasOperatorName(",")))))))
+ .bind("sizeof-comma-expr"),
+ this);
+
+ // Detect sizeof(...) /sizeof(...));
+ const auto ElemType =
+ arrayType(hasElementType(recordType().bind("elem-type")));
+ const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
+ const auto NumType = qualType(hasCanonicalType(
+ type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")));
+ const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type")));
+
+ Finder->addMatcher(
+ binaryOperator(hasOperatorName("/"),
+ hasLHS(expr(ignoringParenImpCasts(
+ anyOf(sizeOfExpr(has(NumType)),
+ sizeOfExpr(has(expr(hasType(NumType)))))))),
+ hasRHS(expr(ignoringParenImpCasts(
+ anyOf(sizeOfExpr(has(DenomType)),
+ sizeOfExpr(has(expr(hasType(DenomType)))))))))
+ .bind("sizeof-divide-expr"),
+ this);
+
+ // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
+ Finder->addMatcher(binaryOperator(hasOperatorName("*"),
+ hasLHS(ignoringParenImpCasts(SizeOfExpr)),
+ hasRHS(ignoringParenImpCasts(SizeOfExpr)))
+ .bind("sizeof-multiply-sizeof"),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(hasOperatorName("*"),
+ hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
+ hasEitherOperand(ignoringParenImpCasts(binaryOperator(
+ hasOperatorName("*"),
+ hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))
+ .bind("sizeof-multiply-sizeof"),
+ this);
+
+ // Detect strange double-sizeof expression like: sizeof(sizeof(...));
+ // Note: The expression 'sizeof(sizeof(0))' is accepted.
+ Finder->addMatcher(
+ expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
+ hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
+ .bind("sizeof-sizeof-expr"),
+ this);
+}
+
+void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Ctx = *Result.Context;
+
+ if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
+ diag(E->getLocStart(),
+ "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
+ } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
+ diag(E->getLocStart(),
+ "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
+ } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
+ diag(E->getLocStart(),
+ "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
+ diag(E->getLocStart(),
+ "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
+ diag(E->getLocStart(),
+ "suspicious comparison of 'sizeof(expr)' to a constant");
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
+ diag(E->getLocStart(), "suspicious usage of 'sizeof(..., ...)'");
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
+ const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
+ const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
+ const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
+ const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
+
+ CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
+ CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
+ CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
+
+ if (DenominatorSize > CharUnits::Zero() &&
+ !NumeratorSize.isMultipleOf(DenominatorSize)) {
+ diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
+ " numerator is not a multiple of denominator");
+ } else if (ElementSize > CharUnits::Zero() &&
+ DenominatorSize > CharUnits::Zero() &&
+ ElementSize != DenominatorSize) {
+ diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
+ " numerator is not a multiple of denominator");
+ } else if (NumTy && DenomTy && NumTy == DenomTy) {
+ diag(E->getLocStart(),
+ "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
+ } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
+ diag(E->getLocStart(),
+ "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
+ } else if (NumTy && DenomTy && NumTy->isPointerType() &&
+ DenomTy->isPointerType()) {
+ diag(E->getLocStart(),
+ "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
+ }
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
+ diag(E->getLocStart(), "suspicious usage of 'sizeof(sizeof(...))'");
+ } else if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
+ diag(E->getLocStart(), "suspicious 'sizeof' by 'sizeof' multiplication");
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SizeofExpressionCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious usages of sizeof expression.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-sizeof-expression.html
+class SizeofExpressionCheck : public ClangTidyCheck {
+public:
+ SizeofExpressionCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool WarnOnSizeOfConstant;
+ const bool WarnOnSizeOfThis;
+ const bool WarnOnSizeOfCompareToConstant;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
--- /dev/null
+//===--- StaticAssertCheck.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 "StaticAssertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
+ // This checker only makes sense for languages that have static assertion
+ // capabilities: C++11 and C11.
+ if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11))
+ return;
+
+ auto NegatedString = unaryOperator(
+ hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));
+ auto IsAlwaysFalse =
+ expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
+ cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))
+ .bind("isAlwaysFalse");
+ auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
+ IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))
+ .bind("castExpr")));
+ auto AssertExprRoot = anyOf(
+ binaryOperator(
+ anyOf(hasOperatorName("&&"), hasOperatorName("==")),
+ hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),
+ anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
+ anything()))
+ .bind("assertExprRoot"),
+ IsAlwaysFalse);
+ auto NonConstexprFunctionCall =
+ callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
+ auto AssertCondition =
+ expr(
+ anyOf(expr(ignoringParenCasts(anyOf(
+ AssertExprRoot, unaryOperator(hasUnaryOperand(
+ ignoringParenCasts(AssertExprRoot)))))),
+ anything()),
+ unless(findAll(NonConstexprFunctionCall)))
+ .bind("condition");
+ auto Condition =
+ anyOf(ignoringParenImpCasts(callExpr(
+ hasDeclaration(functionDecl(hasName("__builtin_expect"))),
+ hasArgument(0, AssertCondition))),
+ AssertCondition);
+
+ Finder->addMatcher(conditionalOperator(hasCondition(Condition),
+ unless(isInTemplateInstantiation()))
+ .bind("condStmt"),
+ this);
+
+ Finder->addMatcher(
+ ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation()))
+ .bind("condStmt"),
+ this);
+}
+
+void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext *ASTCtx = Result.Context;
+ const LangOptions &Opts = ASTCtx->getLangOpts();
+ const SourceManager &SM = ASTCtx->getSourceManager();
+ const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");
+ const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");
+ const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");
+ const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");
+ const auto *AssertExprRoot =
+ Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");
+ const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");
+ SourceLocation AssertExpansionLoc = CondStmt->getLocStart();
+
+ if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
+ return;
+
+ StringRef MacroName =
+ Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
+
+ if (MacroName != "assert" || Condition->isValueDependent() ||
+ Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
+ !Condition->isEvaluatable(*ASTCtx))
+ return;
+
+ // False literal is not the result of macro expansion.
+ if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
+ SourceLocation FalseLiteralLoc =
+ SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
+ if (!FalseLiteralLoc.isMacroID())
+ return;
+
+ StringRef FalseMacroName =
+ Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
+ if (FalseMacroName.compare_lower("false") == 0 ||
+ FalseMacroName.compare_lower("null") == 0)
+ return;
+ }
+
+ SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
+
+ SmallVector<FixItHint, 4> FixItHints;
+ SourceLocation LastParenLoc;
+ if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
+ (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
+ FixItHints.push_back(
+ FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));
+
+ std::string StaticAssertMSG = ", \"\"";
+ if (AssertExprRoot) {
+ FixItHints.push_back(FixItHint::CreateRemoval(
+ SourceRange(AssertExprRoot->getOperatorLoc())));
+ FixItHints.push_back(FixItHint::CreateRemoval(
+ SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd())));
+ StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str();
+ }
+
+ FixItHints.push_back(
+ FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
+ }
+
+ diag(AssertLoc, "found assert() that could be replaced by static_assert()")
+ << FixItHints;
+}
+
+SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,
+ SourceLocation AssertLoc) {
+ const LangOptions &Opts = ASTCtx->getLangOpts();
+ const SourceManager &SM = ASTCtx->getSourceManager();
+
+ llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
+ if (!Buffer)
+ return SourceLocation();
+
+ const char *BufferPos = SM.getCharacterData(AssertLoc);
+
+ Token Token;
+ Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
+ Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
+
+ // assert first left parenthesis
+ if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
+ !Token.is(tok::l_paren))
+ return SourceLocation();
+
+ unsigned int ParenCount = 1;
+ while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
+ if (Token.is(tok::l_paren))
+ ++ParenCount;
+ else if (Token.is(tok::r_paren))
+ --ParenCount;
+ }
+
+ return Token.getLocation();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StaticAssertCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Replaces `assert()` with `static_assert()` if the condition is evaluatable
+/// at compile time.
+///
+/// The condition of `static_assert()` is evaluated at compile time which is
+/// safer and more efficient.
+class StaticAssertCheck : public ClangTidyCheck {
+public:
+ StaticAssertCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ SourceLocation getLastParenLoc(const ASTContext *ASTCtx,
+ SourceLocation AssertLoc);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
--- /dev/null
+//===--- MiscStringCompare.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 "StringCompareCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static const StringRef CompareMessage = "do not use 'compare' to test equality "
+ "of strings; use the string equality "
+ "operator instead";
+
+void StringCompareCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto StrCompare = cxxMemberCallExpr(
+ callee(cxxMethodDecl(hasName("compare"),
+ ofClass(classTemplateSpecializationDecl(
+ hasName("::std::basic_string"))))),
+ hasArgument(0, expr().bind("str2")), argumentCountIs(1),
+ callee(memberExpr().bind("str1")));
+
+ // First and second case: cast str.compare(str) to boolean.
+ Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
+ has(StrCompare))
+ .bind("match1"),
+ this);
+
+ // Third and fourth case: str.compare(str) == 0 and str.compare(str) != 0.
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+ hasEitherOperand(StrCompare.bind("compare")),
+ hasEitherOperand(integerLiteral(equals(0)).bind("zero")))
+ .bind("match2"),
+ this);
+}
+
+void StringCompareCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match1")) {
+ diag(Matched->getLocStart(), CompareMessage);
+ return;
+ }
+
+ if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match2")) {
+ const ASTContext &Ctx = *Result.Context;
+
+ if (const auto *Zero = Result.Nodes.getNodeAs<Stmt>("zero")) {
+ const auto *Str1 = Result.Nodes.getNodeAs<MemberExpr>("str1");
+ const auto *Str2 = Result.Nodes.getNodeAs<Stmt>("str2");
+ const auto *Compare = Result.Nodes.getNodeAs<Stmt>("compare");
+
+ auto Diag = diag(Matched->getLocStart(), CompareMessage);
+
+ if (Str1->isArrow())
+ Diag << FixItHint::CreateInsertion(Str1->getLocStart(), "*");
+
+ Diag << tooling::fixit::createReplacement(*Zero, *Str2, Ctx)
+ << tooling::fixit::createReplacement(*Compare, *Str1->getBase(),
+ Ctx);
+ }
+ }
+
+ // FIXME: Add fixit to fix the code for case one and two (match1).
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StringCompareCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check flags all calls compare when used to check for string
+/// equality or inequality.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-compare.html
+class StringCompareCheck : public ClangTidyCheck {
+public:
+ StringCompareCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
--- /dev/null
+//===--- StringConstructorCheck.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 "StringConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
+ return Node.getValue().getZExtValue() > N;
+}
+
+StringConstructorCheck::StringConstructorCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnLargeLength(Options.get("WarnOnLargeLength", 1) != 0),
+ LargeLengthThreshold(Options.get("LargeLengthThreshold", 0x800000)) {}
+
+void StringConstructorCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnLargeLength", WarnOnLargeLength);
+ Options.store(Opts, "LargeLengthThreshold", LargeLengthThreshold);
+}
+
+void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto ZeroExpr = expr(ignoringParenImpCasts(integerLiteral(equals(0))));
+ const auto CharExpr = expr(ignoringParenImpCasts(characterLiteral()));
+ const auto NegativeExpr = expr(ignoringParenImpCasts(
+ unaryOperator(hasOperatorName("-"),
+ hasUnaryOperand(integerLiteral(unless(equals(0)))))));
+ const auto LargeLengthExpr = expr(ignoringParenImpCasts(
+ integerLiteral(isBiggerThan(LargeLengthThreshold))));
+ const auto CharPtrType = type(anyOf(pointerType(), arrayType()));
+
+ // Match a string-literal; even through a declaration with initializer.
+ const auto BoundStringLiteral = stringLiteral().bind("str");
+ const auto ConstStrLiteralDecl = varDecl(
+ isDefinition(), hasType(constantArrayType()), hasType(isConstQualified()),
+ hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
+ const auto ConstPtrStrLiteralDecl = varDecl(
+ isDefinition(),
+ hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))),
+ hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
+ const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf(
+ BoundStringLiteral, declRefExpr(hasDeclaration(anyOf(
+ ConstPtrStrLiteralDecl, ConstStrLiteralDecl))))));
+
+ // Check the fill constructor. Fills the string with n consecutive copies of
+ // character c. [i.e string(size_t n, char c);].
+ Finder->addMatcher(
+ cxxConstructExpr(
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ hasArgument(0, hasType(qualType(isInteger()))),
+ hasArgument(1, hasType(qualType(isInteger()))),
+ anyOf(
+ // Detect the expression: string('x', 40);
+ hasArgument(0, CharExpr.bind("swapped-parameter")),
+ // Detect the expression: string(0, ...);
+ hasArgument(0, ZeroExpr.bind("empty-string")),
+ // Detect the expression: string(-4, ...);
+ hasArgument(0, NegativeExpr.bind("negative-length")),
+ // Detect the expression: string(0x1234567, ...);
+ hasArgument(0, LargeLengthExpr.bind("large-length"))))
+ .bind("constructor"),
+ this);
+
+ // Check the literal string constructor with char pointer and length
+ // parameters. [i.e. string (const char* s, size_t n);]
+ Finder->addMatcher(
+ cxxConstructExpr(
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ hasArgument(0, hasType(CharPtrType)),
+ hasArgument(1, hasType(isInteger())),
+ anyOf(
+ // Detect the expression: string("...", 0);
+ hasArgument(1, ZeroExpr.bind("empty-string")),
+ // Detect the expression: string("...", -4);
+ hasArgument(1, NegativeExpr.bind("negative-length")),
+ // Detect the expression: string("lit", 0x1234567);
+ hasArgument(1, LargeLengthExpr.bind("large-length")),
+ // Detect the expression: string("lit", 5)
+ allOf(hasArgument(0, ConstStrLiteral.bind("literal-with-length")),
+ hasArgument(1, ignoringParenImpCasts(
+ integerLiteral().bind("int"))))))
+ .bind("constructor"),
+ this);
+}
+
+void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Ctx = *Result.Context;
+ const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("constructor");
+ assert(E && "missing constructor expression");
+ SourceLocation Loc = E->getLocStart();
+
+ if (Result.Nodes.getNodeAs<Expr>("swapped-parameter")) {
+ const Expr *P0 = E->getArg(0);
+ const Expr *P1 = E->getArg(1);
+ diag(Loc, "string constructor parameters are probably swapped;"
+ " expecting string(count, character)")
+ << tooling::fixit::createReplacement(*P0, *P1, Ctx)
+ << tooling::fixit::createReplacement(*P1, *P0, Ctx);
+ } else if (Result.Nodes.getNodeAs<Expr>("empty-string")) {
+ diag(Loc, "constructor creating an empty string");
+ } else if (Result.Nodes.getNodeAs<Expr>("negative-length")) {
+ diag(Loc, "negative value used as length parameter");
+ } else if (Result.Nodes.getNodeAs<Expr>("large-length")) {
+ if (WarnOnLargeLength)
+ diag(Loc, "suspicious large length parameter");
+ } else if (Result.Nodes.getNodeAs<Expr>("literal-with-length")) {
+ const auto *Str = Result.Nodes.getNodeAs<StringLiteral>("str");
+ const auto *Lit = Result.Nodes.getNodeAs<IntegerLiteral>("int");
+ if (Lit->getValue().ugt(Str->getLength())) {
+ diag(Loc, "length is bigger then string literal size");
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StringConstructorCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds suspicious string constructor and check their parameters.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-constructor.html
+class StringConstructorCheck : public ClangTidyCheck {
+public:
+ StringConstructorCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool WarnOnLargeLength;
+ const unsigned int LargeLengthThreshold;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
--- /dev/null
+//===--- StringIntegerAssignmentCheck.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 "StringIntegerAssignmentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ anyOf(hasOverloadedOperatorName("="),
+ hasOverloadedOperatorName("+=")),
+ callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
+ hasName("::std::basic_string"),
+ hasTemplateArgument(0, refersToType(qualType().bind("type"))))))),
+ hasArgument(1,
+ ignoringImpCasts(expr(hasType(isInteger()),
+ unless(hasType(isAnyCharacter())))
+ .bind("expr"))),
+ unless(isInTemplateInstantiation())),
+ this);
+}
+
+void StringIntegerAssignmentCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr");
+ SourceLocation Loc = Argument->getLocStart();
+
+ auto Diag =
+ diag(Loc, "an integer is interpreted as a character code when assigning "
+ "it to a string; if this is intended, cast the integer to the "
+ "appropriate character type; if you want a string "
+ "representation, use the appropriate conversion facility");
+
+ if (Loc.isMacroID())
+ return;
+
+ auto CharType = *Result.Nodes.getNodeAs<QualType>("type");
+ bool IsWideCharType = CharType->isWideCharType();
+ if (!CharType->isCharType() && !IsWideCharType)
+ return;
+ bool IsOneDigit = false;
+ bool IsLiteral = false;
+ if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) {
+ IsOneDigit = Literal->getValue().getLimitedValue() < 10;
+ IsLiteral = true;
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Argument->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+ if (IsOneDigit) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'")
+ << FixItHint::CreateInsertion(EndLoc, "'");
+ return;
+ }
+ if (IsLiteral) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"")
+ << FixItHint::CreateInsertion(EndLoc, "\"");
+ return;
+ }
+
+ if (getLangOpts().CPlusPlus11) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring("
+ : "std::to_string(")
+ << FixItHint::CreateInsertion(EndLoc, ")");
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StringIntegerAssignmentCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds instances where an integer is assigned to a string.
+///
+/// For more details see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-assignment.html
+class StringIntegerAssignmentCheck : public ClangTidyCheck {
+public:
+ StringIntegerAssignmentCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
--- /dev/null
+//===--- StringLiteralWithEmbeddedNulCheck.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 "StringLiteralWithEmbeddedNulCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER(StringLiteral, containsNul) {
+ for (size_t i = 0; i < Node.getLength(); ++i)
+ if (Node.getCodeUnit(i) == '\0')
+ return true;
+ return false;
+}
+
+void StringLiteralWithEmbeddedNulCheck::registerMatchers(MatchFinder *Finder) {
+ // Match a string that contains embedded NUL character. Extra-checks are
+ // applied in |check| to find incorectly escaped characters.
+ Finder->addMatcher(stringLiteral(containsNul()).bind("strlit"), this);
+
+ // The remaining checks only apply to C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto StrLitWithNul =
+ ignoringParenImpCasts(stringLiteral(containsNul()).bind("truncated"));
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(anyOf(
+ cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+ // If present, the second argument is the alloc object which must not
+ // be present explicitly.
+ cxxConstructExpr(argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Detect passing a suspicious string literal to a string constructor.
+ // example: std::string str = "abc\0def";
+ Finder->addMatcher(
+ cxxConstructExpr(StringConstructorExpr, hasArgument(0, StrLitWithNul)),
+ this);
+
+ // Detect passing a suspicious string literal through an overloaded operator.
+ Finder->addMatcher(cxxOperatorCallExpr(hasAnyArgument(StrLitWithNul)), this);
+}
+
+void StringLiteralWithEmbeddedNulCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("strlit")) {
+ for (size_t Offset = 0, Length = SL->getLength(); Offset < Length;
+ ++Offset) {
+ // Find a sequence of character like "\0x12".
+ if (Offset + 3 < Length && SL->getCodeUnit(Offset) == '\0' &&
+ SL->getCodeUnit(Offset + 1) == 'x' &&
+ isDigit(SL->getCodeUnit(Offset + 2)) &&
+ isDigit(SL->getCodeUnit(Offset + 3))) {
+ diag(SL->getLocStart(), "suspicious embedded NUL character");
+ return;
+ }
+ }
+ }
+
+ if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("truncated")) {
+ diag(SL->getLocStart(),
+ "truncated string literal with embedded NUL character");
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StringLiteralWithEmbeddedNulCheck.h - clang-tidy--------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious string literals with embedded NUL characters.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-literal-with-embedded-nul.html
+class StringLiteralWithEmbeddedNulCheck : public ClangTidyCheck {
+public:
+ StringLiteralWithEmbeddedNulCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
--- /dev/null
+//===--- SuspiciousEnumUsageCheck.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 "SuspiciousEnumUsageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static const char DifferentEnumErrorMessage[] =
+ "enum values are from different enum types";
+
+static const char BitmaskErrorMessage[] =
+ "enum type seems like a bitmask (contains mostly "
+ "power-of-2 literals), but this literal is not a "
+ "power-of-2";
+
+static const char BitmaskVarErrorMessage[] =
+ "enum type seems like a bitmask (contains mostly "
+ "power-of-2 literals) but %plural{1:a literal is|:some literals are}0 not "
+ "power-of-2";
+
+static const char BitmaskNoteMessage[] = "used here as a bitmask";
+
+/// Stores a min and a max value which describe an interval.
+struct ValueRange {
+ llvm::APSInt MinVal;
+ llvm::APSInt MaxVal;
+
+ ValueRange(const EnumDecl *EnumDec) {
+ const auto MinMaxVal = std::minmax_element(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+ return E1->getInitVal() < E2->getInitVal();
+ });
+ MinVal = MinMaxVal.first->getInitVal();
+ MaxVal = MinMaxVal.second->getInitVal();
+ }
+};
+
+/// Return the number of EnumConstantDecls in an EnumDecl.
+static int enumLength(const EnumDecl *EnumDec) {
+ return std::distance(EnumDec->enumerator_begin(), EnumDec->enumerator_end());
+}
+
+static bool hasDisjointValueRange(const EnumDecl *Enum1,
+ const EnumDecl *Enum2) {
+ ValueRange Range1(Enum1), Range2(Enum2);
+ return (Range1.MaxVal < Range2.MinVal) || (Range2.MaxVal < Range1.MinVal);
+}
+
+static bool isNonPowerOf2NorNullLiteral(const EnumConstantDecl *EnumConst) {
+ llvm::APSInt Val = EnumConst->getInitVal();
+ if (Val.isPowerOf2() || !Val.getBoolValue())
+ return false;
+ const Expr *InitExpr = EnumConst->getInitExpr();
+ if (!InitExpr)
+ return true;
+ return isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+}
+
+static bool isMaxValAllBitSetLiteral(const EnumDecl *EnumDec) {
+ auto EnumConst = std::max_element(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+ return E1->getInitVal() < E2->getInitVal();
+ });
+
+ if (const Expr *InitExpr = EnumConst->getInitExpr()) {
+ return EnumConst->getInitVal().countTrailingOnes() ==
+ EnumConst->getInitVal().getActiveBits() &&
+ isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+ }
+ return false;
+}
+
+static int countNonPowOfTwoLiteralNum(const EnumDecl *EnumDec) {
+ return std::count_if(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E) { return isNonPowerOf2NorNullLiteral(E); });
+}
+
+/// Check if there is one or two enumerators that are not a power of 2 and are
+/// initialized by a literal in the enum type, and that the enumeration contains
+/// enough elements to reasonably act as a bitmask. Exclude the case where the
+/// last enumerator is the sum of the lesser values (and initialized by a
+/// literal) or when it could contain consecutive values.
+static bool isPossiblyBitMask(const EnumDecl *EnumDec) {
+ ValueRange VR(EnumDec);
+ int EnumLen = enumLength(EnumDec);
+ int NonPowOfTwoCounter = countNonPowOfTwoLiteralNum(EnumDec);
+ return NonPowOfTwoCounter >= 1 && NonPowOfTwoCounter <= 2 &&
+ NonPowOfTwoCounter < EnumLen / 2 &&
+ (VR.MaxVal - VR.MinVal != EnumLen - 1) &&
+ !(NonPowOfTwoCounter == 1 && isMaxValAllBitSetLiteral(EnumDec));
+}
+
+SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
+
+void SuspiciousEnumUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StrictMode", StrictMode);
+}
+
+void SuspiciousEnumUsageCheck::registerMatchers(MatchFinder *Finder) {
+ const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
+ return allOf(ignoringImpCasts(expr().bind(RefName)),
+ ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
+ };
+
+ Finder->addMatcher(
+ binaryOperator(hasOperatorName("|"), hasLHS(enumExpr("", "enumDecl")),
+ hasRHS(allOf(enumExpr("", "otherEnumDecl"),
+ ignoringImpCasts(hasType(enumDecl(
+ unless(equalsBoundNode("enumDecl"))))))))
+ .bind("diffEnumOp"),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+ hasLHS(enumExpr("lhsExpr", "enumDecl")),
+ hasRHS(allOf(enumExpr("rhsExpr", ""),
+ ignoringImpCasts(hasType(enumDecl(
+ equalsBoundNode("enumDecl"))))))),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+ hasEitherOperand(
+ allOf(hasType(isInteger()), unless(enumExpr("", "")))),
+ hasEitherOperand(enumExpr("enumExpr", "enumDecl"))),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("+=")),
+ hasRHS(enumExpr("enumExpr", "enumDecl"))),
+ this);
+}
+
+void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
+ const Expr *NodeExpr, const EnumDecl *EnumDec) {
+ const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
+ const auto *EnumConst =
+ EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) : nullptr;
+
+ // Report the parameter if neccessary.
+ if (!EnumConst) {
+ diag(EnumDec->getInnerLocStart(), BitmaskVarErrorMessage)
+ << countNonPowOfTwoLiteralNum(EnumDec);
+ diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+ } else if (isNonPowerOf2NorNullLiteral(EnumConst)) {
+ diag(EnumConst->getSourceRange().getBegin(), BitmaskErrorMessage);
+ diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+ }
+}
+
+void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) {
+ // Case 1: The two enum values come from different types.
+ if (const auto *DiffEnumOp =
+ Result.Nodes.getNodeAs<BinaryOperator>("diffEnumOp")) {
+ const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+ const auto *OtherEnumDec =
+ Result.Nodes.getNodeAs<EnumDecl>("otherEnumDecl");
+ // Skip when one of the parameters is an empty enum. The
+ // hasDisjointValueRange function could not decide the values properly in
+ // case of an empty enum.
+ if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
+ OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
+ return;
+
+ if (!hasDisjointValueRange(EnumDec, OtherEnumDec))
+ diag(DiffEnumOp->getOperatorLoc(), DifferentEnumErrorMessage);
+ return;
+ }
+
+ // Case 2 and 3 only checked in strict mode. The checker tries to detect
+ // suspicious bitmasks which contains values initialized by non power-of-2
+ // literals.
+ if (!StrictMode)
+ return;
+ const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+ if (!isPossiblyBitMask(EnumDec))
+ return;
+
+ // Case 2:
+ // a. Investigating the right hand side of `+=` or `|=` operator.
+ // b. When the operator is `|` or `+` but only one of them is an EnumExpr
+ if (const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>("enumExpr")) {
+ checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
+ return;
+ }
+
+ // Case 3:
+ // '|' or '+' operator where both argument comes from the same enum type
+ const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>("lhsExpr");
+ checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
+
+ const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>("rhsExpr");
+ checkSuspiciousBitmaskUsage(RhsExpr, EnumDec);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SuspiciousEnumUsageCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The checker detects various cases when an enum is probably misused (as a
+/// bitmask).
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-enum-usage.html
+class SuspiciousEnumUsageCheck : public ClangTidyCheck {
+public:
+ SuspiciousEnumUsageCheck(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:
+ void checkSuspiciousBitmaskUsage(const Expr*, const EnumDecl*);
+ const bool StrictMode;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
--- /dev/null
+//===--- SuspiciousMissingCommaCheck.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 "SuspiciousMissingCommaCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
+ const StringLiteral *Lit) {
+ // String literals surrounded by parentheses are assumed to be on purpose.
+ // i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] };
+ auto Parents = Ctx->getParents(*Lit);
+ if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr)
+ return true;
+
+ // Appropriately indented string literals are assumed to be on purpose.
+ // The following frequent indentation is accepted:
+ // const char* Array[] = {
+ // "first literal"
+ // "indented literal"
+ // "indented literal",
+ // "second literal",
+ // [...]
+ // };
+ const SourceManager &SM = Ctx->getSourceManager();
+ bool IndentedCorrectly = true;
+ SourceLocation FirstToken = Lit->getStrTokenLoc(0);
+ FileID BaseFID = SM.getFileID(FirstToken);
+ unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);
+ unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);
+ for (unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++TokNum) {
+ SourceLocation Token = Lit->getStrTokenLoc(TokNum);
+ FileID FID = SM.getFileID(Token);
+ unsigned int Indent = SM.getSpellingColumnNumber(Token);
+ unsigned int Line = SM.getSpellingLineNumber(Token);
+ if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) {
+ IndentedCorrectly = false;
+ break;
+ }
+ }
+ if (IndentedCorrectly)
+ return true;
+
+ // There is no pattern recognized by the checker, assume it's not on purpose.
+ return false;
+}
+
+AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
+ MaxConcatenatedTokens) {
+ return Node.getNumConcatenated() > 1 &&
+ Node.getNumConcatenated() < MaxConcatenatedTokens &&
+ !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);
+}
+
+} // namespace
+
+SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ SizeThreshold(Options.get("SizeThreshold", 5U)),
+ RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))),
+ MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {}
+
+void SuspiciousMissingCommaCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "SizeThreshold", SizeThreshold);
+ Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold));
+ Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens);
+}
+
+void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ConcatenatedStringLiteral =
+ stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str");
+
+ const auto StringsInitializerList =
+ initListExpr(hasType(constantArrayType()),
+ has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
+
+ Finder->addMatcher(StringsInitializerList.bind("list"), this);
+}
+
+void SuspiciousMissingCommaCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>("list");
+ const auto *ConcatenatedLiteral =
+ Result.Nodes.getNodeAs<StringLiteral>("str");
+ assert(InitializerList && ConcatenatedLiteral);
+
+ // Skip small arrays as they often generate false-positive.
+ unsigned int Size = InitializerList->getNumInits();
+ if (Size < SizeThreshold)
+ return;
+
+ // Count the number of occurence of concatenated string literal.
+ unsigned int Count = 0;
+ for (unsigned int i = 0; i < Size; ++i) {
+ const Expr *Child = InitializerList->getInit(i)->IgnoreImpCasts();
+ if (const auto *Literal = dyn_cast<StringLiteral>(Child)) {
+ if (Literal->getNumConcatenated() > 1)
+ ++Count;
+ }
+ }
+
+ // Warn only when concatenation is not common in this initializer list.
+ // The current threshold is set to less than 1/5 of the string literals.
+ if (double(Count) / Size > RatioThreshold)
+ return;
+
+ diag(ConcatenatedLiteral->getLocStart(),
+ "suspicious string literal, probably missing a comma");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SuspiciousMissingCommaCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check finds string literals which are probably concatenated
+/// accidentally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-missing-comma.html
+class SuspiciousMissingCommaCheck : public ClangTidyCheck {
+public:
+ SuspiciousMissingCommaCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ // Minimal size of a string literals array to be considered by the checker.
+ const unsigned SizeThreshold;
+ // Maximal threshold ratio of suspicious string literals to be considered.
+ const double RatioThreshold;
+ // Maximal number of concatenated tokens.
+ const unsigned MaxConcatenatedTokens;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
--- /dev/null
+//===--- SuspiciousSemicolonCheck.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 "SuspiciousSemicolonCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SuspiciousSemicolonCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ stmt(anyOf(ifStmt(hasThen(nullStmt().bind("semi")),
+ unless(hasElse(stmt()))),
+ forStmt(hasBody(nullStmt().bind("semi"))),
+ cxxForRangeStmt(hasBody(nullStmt().bind("semi"))),
+ whileStmt(hasBody(nullStmt().bind("semi")))))
+ .bind("stmt"),
+ this);
+}
+
+void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
+ if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+ return;
+
+ const auto *Semicolon = Result.Nodes.getNodeAs<NullStmt>("semi");
+ SourceLocation LocStart = Semicolon->getLocStart();
+
+ if (LocStart.isMacroID())
+ return;
+
+ ASTContext &Ctxt = *Result.Context;
+ auto Token = utils::lexer::getPreviousToken(Ctxt, LocStart);
+ auto &SM = *Result.SourceManager;
+ unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart);
+
+ const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");
+ const bool IsIfStmt = isa<IfStmt>(Statement);
+
+ if (!IsIfStmt &&
+ SM.getSpellingLineNumber(Token.getLocation()) != SemicolonLine)
+ return;
+
+ SourceLocation LocEnd = Semicolon->getLocEnd();
+ FileID FID = SM.getFileID(LocEnd);
+ llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd);
+ Lexer Lexer(SM.getLocForStartOfFile(FID), Ctxt.getLangOpts(),
+ Buffer->getBufferStart(), SM.getCharacterData(LocEnd) + 1,
+ Buffer->getBufferEnd());
+ if (Lexer.LexFromRawLexer(Token))
+ return;
+
+ unsigned BaseIndent = SM.getSpellingColumnNumber(Statement->getLocStart());
+ unsigned NewTokenIndent = SM.getSpellingColumnNumber(Token.getLocation());
+ unsigned NewTokenLine = SM.getSpellingLineNumber(Token.getLocation());
+
+ if (!IsIfStmt && NewTokenIndent <= BaseIndent &&
+ Token.getKind() != tok::l_brace && NewTokenLine != SemicolonLine)
+ return;
+
+ diag(LocStart, "potentially unintended semicolon")
+ << FixItHint::CreateRemoval(SourceRange(LocStart, LocEnd));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SuspiciousSemicolonCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check finds semicolon that modifies the meaning of the program
+/// unintendedly.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-semicolon.html
+class SuspiciousSemicolonCheck : public ClangTidyCheck {
+public:
+ SuspiciousSemicolonCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
--- /dev/null
+//===--- SuspiciousStringCompareCheck.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 "SuspiciousStringCompareCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+// Semicolon separated list of known string compare-like functions. The list
+// must ends with a semicolon.
+static const char KnownStringCompareFunctions[] = "__builtin_memcmp;"
+ "__builtin_strcasecmp;"
+ "__builtin_strcmp;"
+ "__builtin_strncasecmp;"
+ "__builtin_strncmp;"
+ "_mbscmp;"
+ "_mbscmp_l;"
+ "_mbsicmp;"
+ "_mbsicmp_l;"
+ "_mbsnbcmp;"
+ "_mbsnbcmp_l;"
+ "_mbsnbicmp;"
+ "_mbsnbicmp_l;"
+ "_mbsncmp;"
+ "_mbsncmp_l;"
+ "_mbsnicmp;"
+ "_mbsnicmp_l;"
+ "_memicmp;"
+ "_memicmp_l;"
+ "_stricmp;"
+ "_stricmp_l;"
+ "_strnicmp;"
+ "_strnicmp_l;"
+ "_wcsicmp;"
+ "_wcsicmp_l;"
+ "_wcsnicmp;"
+ "_wcsnicmp_l;"
+ "lstrcmp;"
+ "lstrcmpi;"
+ "memcmp;"
+ "memicmp;"
+ "strcasecmp;"
+ "strcmp;"
+ "strcmpi;"
+ "stricmp;"
+ "strncasecmp;"
+ "strncmp;"
+ "strnicmp;"
+ "wcscasecmp;"
+ "wcscmp;"
+ "wcsicmp;"
+ "wcsncmp;"
+ "wcsnicmp;"
+ "wmemcmp;";
+
+SuspiciousStringCompareCheck::SuspiciousStringCompareCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnImplicitComparison(Options.get("WarnOnImplicitComparison", 1)),
+ WarnOnLogicalNotComparison(Options.get("WarnOnLogicalNotComparison", 0)),
+ StringCompareLikeFunctions(
+ Options.get("StringCompareLikeFunctions", "")) {}
+
+void SuspiciousStringCompareCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnImplicitComparison", WarnOnImplicitComparison);
+ Options.store(Opts, "WarnOnLogicalNotComparison", WarnOnLogicalNotComparison);
+ Options.store(Opts, "StringCompareLikeFunctions", StringCompareLikeFunctions);
+}
+
+void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) {
+ // Match relational operators.
+ const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!"));
+ const auto ComparisonBinaryOperator =
+ binaryOperator(matchers::isComparisonOperator());
+ const auto ComparisonOperator =
+ expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator));
+
+ // Add the list of known string compare-like functions and add user-defined
+ // functions.
+ std::vector<std::string> FunctionNames = utils::options::parseStringList(
+ (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions)
+ .str());
+
+ // Match a call to a string compare functions.
+ const auto FunctionCompareDecl =
+ functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
+ FunctionNames.end())))
+ .bind("decl");
+ const auto DirectStringCompareCallExpr =
+ callExpr(hasDeclaration(FunctionCompareDecl)).bind("call");
+ const auto MacroStringCompareCallExpr = conditionalOperator(anyOf(
+ hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)),
+ hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr))));
+ // The implicit cast is not present in C.
+ const auto StringCompareCallExpr = ignoringParenImpCasts(
+ anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr));
+
+ if (WarnOnImplicitComparison) {
+ // Detect suspicious calls to string compare:
+ // 'if (strcmp())' -> 'if (strcmp() != 0)'
+ Finder->addMatcher(
+ stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
+ whileStmt(hasCondition(StringCompareCallExpr)),
+ doStmt(hasCondition(StringCompareCallExpr)),
+ forStmt(hasCondition(StringCompareCallExpr)),
+ binaryOperator(
+ anyOf(hasOperatorName("&&"), hasOperatorName("||")),
+ hasEitherOperand(StringCompareCallExpr))))
+ .bind("missing-comparison"),
+ this);
+ }
+
+ if (WarnOnLogicalNotComparison) {
+ // Detect suspicious calls to string compared with '!' operator:
+ // 'if (!strcmp())' -> 'if (strcmp() == 0)'
+ Finder->addMatcher(unaryOperator(hasOperatorName("!"),
+ hasUnaryOperand(ignoringParenImpCasts(
+ StringCompareCallExpr)))
+ .bind("logical-not-comparison"),
+ this);
+ }
+
+ // Detect suspicious cast to an inconsistant type (i.e. not integer type).
+ Finder->addMatcher(
+ implicitCastExpr(unless(hasType(isInteger())),
+ hasSourceExpression(StringCompareCallExpr))
+ .bind("invalid-conversion"),
+ this);
+
+ // Detect suspicious operator with string compare function as operand.
+ Finder->addMatcher(
+ binaryOperator(
+ unless(anyOf(matchers::isComparisonOperator(), hasOperatorName("&&"),
+ hasOperatorName("||"), hasOperatorName("="))),
+ hasEitherOperand(StringCompareCallExpr))
+ .bind("suspicious-operator"),
+ this);
+
+ // Detect comparison to invalid constant: 'strcmp() == -1'.
+ const auto InvalidLiteral = ignoringParenImpCasts(
+ anyOf(integerLiteral(unless(equals(0))),
+ unaryOperator(
+ hasOperatorName("-"),
+ has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))),
+ characterLiteral(), cxxBoolLiteral()));
+
+ Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(),
+ hasEitherOperand(StringCompareCallExpr),
+ hasEitherOperand(InvalidLiteral))
+ .bind("invalid-comparison"),
+ this);
+}
+
+void SuspiciousStringCompareCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ assert(Decl != nullptr && Call != nullptr);
+
+ if (Result.Nodes.getNodeAs<Stmt>("missing-comparison")) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
+ getLangOpts());
+
+ diag(Call->getLocStart(),
+ "function %0 is called without explicitly comparing result")
+ << Decl << FixItHint::CreateInsertion(EndLoc, " != 0");
+ }
+
+ if (const auto *E = Result.Nodes.getNodeAs<Expr>("logical-not-comparison")) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
+ getLangOpts());
+ SourceLocation NotLoc = E->getLocStart();
+
+ diag(Call->getLocStart(),
+ "function %0 is compared using logical not operator")
+ << Decl << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(NotLoc, NotLoc))
+ << FixItHint::CreateInsertion(EndLoc, " == 0");
+ }
+
+ if (Result.Nodes.getNodeAs<Stmt>("invalid-comparison")) {
+ diag(Call->getLocStart(),
+ "function %0 is compared to a suspicious constant")
+ << Decl;
+ }
+
+ if (const auto *BinOp =
+ Result.Nodes.getNodeAs<BinaryOperator>("suspicious-operator")) {
+ diag(Call->getLocStart(), "results of function %0 used by operator '%1'")
+ << Decl << BinOp->getOpcodeStr();
+ }
+
+ if (Result.Nodes.getNodeAs<Stmt>("invalid-conversion")) {
+ diag(Call->getLocStart(), "function %0 has suspicious implicit cast")
+ << Decl;
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SuspiciousStringCompareCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious calls to string compare functions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-string-compare.html
+class SuspiciousStringCompareCheck : public ClangTidyCheck {
+public:
+ SuspiciousStringCompareCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool WarnOnImplicitComparison;
+ const bool WarnOnLogicalNotComparison;
+ const std::string StringCompareLikeFunctions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
--- /dev/null
+//===--- SwappedArgumentsCheck.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 "SwappedArgumentsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SwappedArgumentsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(callExpr().bind("call"), this);
+}
+
+/// \brief Look through lvalue to rvalue and nop casts. This filters out
+/// implicit conversions that have no effect on the input but block our view for
+/// other implicit casts.
+static const Expr *ignoreNoOpCasts(const Expr *E) {
+ if (auto *Cast = dyn_cast<CastExpr>(E))
+ if (Cast->getCastKind() == CK_LValueToRValue ||
+ Cast->getCastKind() == CK_NoOp)
+ return ignoreNoOpCasts(Cast->getSubExpr());
+ return E;
+}
+
+/// \brief Restrict the warning to implicit casts that are most likely
+/// accidental. User defined or integral conversions fit in this category,
+/// lvalue to rvalue or derived to base does not.
+static bool isImplicitCastCandidate(const CastExpr *Cast) {
+ return Cast->getCastKind() == CK_UserDefinedConversion ||
+ Cast->getCastKind() == CK_FloatingToBoolean ||
+ Cast->getCastKind() == CK_FloatingToIntegral ||
+ Cast->getCastKind() == CK_IntegralToBoolean ||
+ Cast->getCastKind() == CK_IntegralToFloating ||
+ Cast->getCastKind() == CK_MemberPointerToBoolean ||
+ Cast->getCastKind() == CK_PointerToBoolean;
+}
+
+void SwappedArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
+ const ASTContext &Ctx = *Result.Context;
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+
+ llvm::SmallPtrSet<const Expr *, 4> UsedArgs;
+ for (unsigned I = 1, E = Call->getNumArgs(); I < E; ++I) {
+ const Expr *LHS = Call->getArg(I - 1);
+ const Expr *RHS = Call->getArg(I);
+
+ // Only need to check RHS, as LHS has already been covered. We don't want to
+ // emit two warnings for a single argument.
+ if (UsedArgs.count(RHS))
+ continue;
+
+ const auto *LHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(LHS));
+ const auto *RHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(RHS));
+
+ // Look if this is a potentially swapped argument pair. First look for
+ // implicit casts.
+ if (!LHSCast || !RHSCast || !isImplicitCastCandidate(LHSCast) ||
+ !isImplicitCastCandidate(RHSCast))
+ continue;
+
+ // If the types that go into the implicit casts match the types of the other
+ // argument in the declaration there is a high probability that the
+ // arguments were swapped.
+ // TODO: We could make use of the edit distance between the argument name
+ // and the name of the passed variable in addition to this type based
+ // heuristic.
+ const Expr *LHSFrom = ignoreNoOpCasts(LHSCast->getSubExpr());
+ const Expr *RHSFrom = ignoreNoOpCasts(RHSCast->getSubExpr());
+ if (LHS->getType() == RHS->getType() ||
+ LHS->getType() != RHSFrom->getType() ||
+ RHS->getType() != LHSFrom->getType())
+ continue;
+
+ // Emit a warning and fix-its that swap the arguments.
+ diag(Call->getLocStart(), "argument with implicit conversion from %0 "
+ "to %1 followed by argument converted from "
+ "%2 to %3, potentially swapped arguments.")
+ << LHS->getType() << LHSFrom->getType() << RHS->getType()
+ << 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);
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SwappedArgumentsCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds potentially swapped arguments by looking at implicit conversions.
+class SwappedArgumentsCheck : public ClangTidyCheck {
+public:
+ SwappedArgumentsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
--- /dev/null
+//===--- ThrowByValueCatchByReferenceCheck.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 "ThrowByValueCatchByReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ CheckAnonymousTemporaries(Options.get("CheckThrowTemporaries", true)) {}
+
+void ThrowByValueCatchByReferenceCheck::registerMatchers(MatchFinder *Finder) {
+ // This is a C++ only check thus we register the matchers only for C++
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(cxxThrowExpr().bind("throw"), this);
+ Finder->addMatcher(cxxCatchStmt().bind("catch"), this);
+}
+
+void ThrowByValueCatchByReferenceCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "CheckThrowTemporaries", true);
+}
+
+void ThrowByValueCatchByReferenceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>("throw"));
+ diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>("catch"),
+ *Result.Context);
+}
+
+bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
+ const DeclRefExpr *declRefExpr) {
+ return isa<ParmVarDecl>(declRefExpr->getDecl());
+}
+
+bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
+ const DeclRefExpr *declRefExpr) {
+ auto *valueDecl = declRefExpr->getDecl();
+ if (auto *varDecl = dyn_cast<VarDecl>(valueDecl))
+ return varDecl->isExceptionVariable();
+ return false;
+}
+
+bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
+ const DeclRefExpr *declRefExpr) {
+ return isFunctionParameter(declRefExpr) || isCatchVariable(declRefExpr);
+}
+
+void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
+ const CXXThrowExpr *throwExpr) {
+ if (!throwExpr)
+ return;
+ auto *subExpr = throwExpr->getSubExpr();
+ if (!subExpr)
+ return;
+ auto qualType = subExpr->getType();
+ if (qualType->isPointerType()) {
+ // The code is throwing a pointer.
+ // In case it is strng literal, it is safe and we return.
+ auto *inner = subExpr->IgnoreParenImpCasts();
+ if (isa<StringLiteral>(inner))
+ return;
+ // If it's a variable from a catch statement, we return as well.
+ auto *declRef = dyn_cast<DeclRefExpr>(inner);
+ if (declRef && isCatchVariable(declRef)) {
+ return;
+ }
+ diag(subExpr->getLocStart(), "throw expression throws a pointer; it should "
+ "throw a non-pointer value instead");
+ }
+ // If the throw statement does not throw by pointer then it throws by value
+ // which is ok.
+ // There are addition checks that emit diagnosis messages if the thrown value
+ // is not an RValue. See:
+ // https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries
+ // This behavior can be influenced by an option.
+
+ // If we encounter a CXXThrowExpr, we move through all casts until you either
+ // encounter a DeclRefExpr or a CXXConstructExpr.
+ // If it's a DeclRefExpr, we emit a message if the referenced variable is not
+ // a catch variable or function parameter.
+ // When encountering a CopyOrMoveConstructor: emit message if after casts,
+ // the expression is a LValue
+ if (CheckAnonymousTemporaries) {
+ bool emit = false;
+ auto *currentSubExpr = subExpr->IgnoreImpCasts();
+ const auto *variableReference = dyn_cast<DeclRefExpr>(currentSubExpr);
+ const auto *constructorCall = dyn_cast<CXXConstructExpr>(currentSubExpr);
+ // If we have a DeclRefExpr, we flag for emitting a diagnosis message in
+ // case the referenced variable is neither a function parameter nor a
+ // variable declared in the catch statement.
+ if (variableReference)
+ emit = !isFunctionOrCatchVar(variableReference);
+ else if (constructorCall &&
+ constructorCall->getConstructor()->isCopyOrMoveConstructor()) {
+ // If we have a copy / move construction, we emit a diagnosis message if
+ // the object that we copy construct from is neither a function parameter
+ // nor a variable declared in a catch statement
+ auto argIter =
+ constructorCall
+ ->arg_begin(); // there's only one for copy constructors
+ auto *currentSubExpr = (*argIter)->IgnoreImpCasts();
+ if (currentSubExpr->isLValue()) {
+ if (auto *tmp = dyn_cast<DeclRefExpr>(currentSubExpr))
+ emit = !isFunctionOrCatchVar(tmp);
+ else if (isa<CallExpr>(currentSubExpr))
+ emit = true;
+ }
+ }
+ if (emit)
+ diag(subExpr->getLocStart(),
+ "throw expression should throw anonymous temporary values instead");
+ }
+}
+
+void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
+ const CXXCatchStmt *catchStmt, ASTContext &context) {
+ if (!catchStmt)
+ return;
+ auto caughtType = catchStmt->getCaughtType();
+ if (caughtType.isNull())
+ return;
+ auto *varDecl = catchStmt->getExceptionDecl();
+ if (const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
+ const char *diagMsgCatchReference = "catch handler catches a pointer value; "
+ "should throw a non-pointer value and "
+ "catch by reference instead";
+ // We do not diagnose when catching pointer to strings since we also allow
+ // throwing string literals.
+ if (!PT->getPointeeType()->isAnyCharacterType())
+ diag(varDecl->getLocStart(), diagMsgCatchReference);
+ } else if (!caughtType->isReferenceType()) {
+ const char *diagMsgCatchReference = "catch handler catches by value; "
+ "should catch by reference instead";
+ // If it's not a pointer and not a reference then it must be caught "by
+ // value". In this case we should emit a diagnosis message unless the type
+ // is trivial.
+ if (!caughtType.isTrivialType(context))
+ diag(varDecl->getLocStart(), diagMsgCatchReference);
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ThrowByValueCatchByReferenceCheck.h - clang-tidy--------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+///\brief checks for locations that do not throw by value
+// or catch by reference.
+// The check is C++ only. It checks that all throw locations
+// throw by value and not by pointer. Additionally it
+// contains an option ("CheckThrowTemporaries", default value "true") that
+// checks that thrown objects are anonymous temporaries. It is also
+// acceptable for this check to throw string literals.
+// This test checks that exceptions are caught by reference
+// and not by value or pointer. It will not warn when catching
+// pointer to char, wchar_t, char16_t or char32_t. This is
+// due to not warning on throwing string literals.
+class ThrowByValueCatchByReferenceCheck : public ClangTidyCheck {
+public:
+ ThrowByValueCatchByReferenceCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void diagnoseThrowLocations(const CXXThrowExpr *throwExpr);
+ void diagnoseCatchLocations(const CXXCatchStmt *catchStmt,
+ ASTContext &context);
+ bool isFunctionParameter(const DeclRefExpr *declRefExpr);
+ bool isCatchVariable(const DeclRefExpr *declRefExpr);
+ bool isFunctionOrCatchVar(const DeclRefExpr *declRefExpr);
+ const bool CheckAnonymousTemporaries;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
--- /dev/null
+//===--- UnconventionalAssignOperatorCheck.cpp - clang-tidy -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnconventionalAssignOperatorCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UnconventionalAssignOperatorCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto HasGoodReturnType = cxxMethodDecl(returns(
+ lValueReferenceType(pointee(unless(isConstQualified()),
+ hasDeclaration(equalsBoundNode("class"))))));
+
+ const auto IsSelf = qualType(
+ anyOf(hasDeclaration(equalsBoundNode("class")),
+ referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))));
+ const auto IsAssign =
+ cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
+ hasName("operator="), ofClass(recordDecl().bind("class")))
+ .bind("method");
+ const auto IsSelfAssign =
+ cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf))))
+ .bind("method");
+
+ Finder->addMatcher(
+ cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"),
+ this);
+
+ const auto BadSelf = referenceType(
+ anyOf(lValueReferenceType(pointee(unless(isConstQualified()))),
+ rValueReferenceType(pointee(isConstQualified()))));
+
+ Finder->addMatcher(
+ cxxMethodDecl(IsSelfAssign,
+ hasParameter(0, parmVarDecl(hasType(BadSelf))))
+ .bind("ArgumentType"),
+ this);
+
+ Finder->addMatcher(
+ cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"),
+ this);
+
+ const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts(
+ anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())),
+ cxxOperatorCallExpr(argumentCountIs(1),
+ callee(unresolvedLookupExpr()),
+ hasArgument(0, cxxThisExpr())))))));
+ const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType);
+
+ Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign))
+ .bind("returnStmt"),
+ this);
+}
+
+void UnconventionalAssignOperatorCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *RetStmt = Result.Nodes.getNodeAs<ReturnStmt>("returnStmt")) {
+ diag(RetStmt->getLocStart(), "operator=() should always return '*this'");
+ } else {
+ static const char *const Messages[][2] = {
+ {"ReturnType", "operator=() should return '%0&'"},
+ {"ArgumentType", "operator=() should take '%0 const&', '%0&&' or '%0'"},
+ {"cv", "operator=() should not be marked '%1'"}};
+
+ const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+ for (const auto &Message : Messages) {
+ if (Result.Nodes.getNodeAs<Decl>(Message[0]))
+ diag(Method->getLocStart(), Message[1])
+ << Method->getParent()->getName()
+ << (Method->isConst() ? "const" : "virtual");
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnconventionalAssignOperatorCheck.h - clang-tidy -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds declarations of assignment operators with the wrong return and/or
+/// argument types and definitions with good return type but wrong return
+/// statements.
+///
+/// * The return type must be `Class&`.
+/// * Works with move-assign and assign by value.
+/// * Private and deleted operators are ignored.
+/// * The operator must always return ``*this``.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unconventional-assign-operator.html
+class UnconventionalAssignOperatorCheck : public ClangTidyCheck {
+public:
+ UnconventionalAssignOperatorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
--- /dev/null
+//===--- UndelegatedConstructor.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 "UndelegatedConstructor.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER_P(Stmt, ignoringTemporaryExpr,
+ ast_matchers::internal::Matcher<Stmt>, InnerMatcher) {
+ const Stmt *E = &Node;
+ for (;;) {
+ // Temporaries with non-trivial dtors.
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
+ E = EWC->getSubExpr();
+ // Temporaries with zero or more than two ctor arguments.
+ else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
+ E = BTE->getSubExpr();
+ // Temporaries with exactly one ctor argument.
+ else if (const auto *FCE = dyn_cast<CXXFunctionalCastExpr>(E))
+ E = FCE->getSubExpr();
+ else
+ break;
+ }
+
+ return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+// Finds a node if it's a base of an already bound node.
+AST_MATCHER_P(CXXRecordDecl, baseOfBoundNode, std::string, ID) {
+ return Builder->removeBindings(
+ [&](const ast_matchers::internal::BoundNodesMap &Nodes) {
+ const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(ID);
+ return Derived != &Node && !Derived->isDerivedFrom(&Node);
+ });
+}
+} // namespace
+
+void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) {
+ // We look for calls to constructors of the same type in constructors. To do
+ // this we have to look through a variety of nodes that occur in the path,
+ // depending on the type's destructor and the number of arguments on the
+ // constructor call, this is handled by ignoringTemporaryExpr. Ignore template
+ // instantiations to reduce the number of duplicated warnings.
+ //
+ // Only register the matchers for C++11; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ compoundStmt(
+ hasParent(
+ cxxConstructorDecl(ofClass(cxxRecordDecl().bind("parent")))),
+ forEach(ignoringTemporaryExpr(
+ cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass(
+ cxxRecordDecl(baseOfBoundNode("parent"))))))
+ .bind("construct"))),
+ unless(isInTemplateInstantiation())),
+ this);
+}
+
+void UndelegatedConstructorCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
+ diag(E->getLocStart(), "did you intend to call a delegated constructor? "
+ "A temporary object is created here instead");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UndelegatedConstructor.h - clang-tidy ------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds creation of temporary objects in constructors that look like a
+/// function call to another constructor of the same class.
+///
+/// The user most likely meant to use a delegating constructor or base class
+/// initializer.
+class UndelegatedConstructorCheck : public ClangTidyCheck {
+public:
+ UndelegatedConstructorCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
--- /dev/null
+//===--- UniqueptrResetReleaseCheck.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 "UniqueptrResetReleaseCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++11; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
+ callee(
+ cxxMethodDecl(hasName("reset"),
+ ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
+ decl().bind("left_class"))))),
+ has(ignoringParenImpCasts(cxxMemberCallExpr(
+ on(expr().bind("right")),
+ callee(memberExpr().bind("release_member")),
+ callee(cxxMethodDecl(
+ hasName("release"),
+ ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
+ decl().bind("right_class")))))))))
+ .bind("reset_call"),
+ this);
+}
+
+namespace {
+const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
+ StringRef ID) {
+ const auto *Class =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
+ if (!Class)
+ return nullptr;
+ auto DeleterArgument = Class->getTemplateArgs()[1];
+ if (DeleterArgument.getKind() != TemplateArgument::Type)
+ return nullptr;
+ return DeleterArgument.getAsType().getTypePtr();
+}
+
+bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
+ const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
+ const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
+
+ if (LeftDeleterType->getUnqualifiedDesugaredType() ==
+ RightDeleterType->getUnqualifiedDesugaredType()) {
+ // Same type. We assume they are compatible.
+ // This check handles the case where the deleters are function pointers.
+ return true;
+ }
+
+ const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
+ const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
+ if (!LeftDeleter || !RightDeleter)
+ return false;
+
+ if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
+ // Same class. We assume they are compatible.
+ return true;
+ }
+
+ const auto *LeftAsTemplate =
+ dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
+ const auto *RightAsTemplate =
+ dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
+ if (LeftAsTemplate && RightAsTemplate &&
+ LeftAsTemplate->getSpecializedTemplate() ==
+ RightAsTemplate->getSpecializedTemplate()) {
+ // They are different instantiations of the same template. We assume they
+ // are compatible.
+ // This handles things like std::default_delete<Base> vs.
+ // std::default_delete<Derived>.
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
+ if (!areDeletersCompatible(Result))
+ return;
+
+ const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
+ const auto *ReleaseMember =
+ Result.Nodes.getNodeAs<MemberExpr>("release_member");
+ const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
+ const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
+ const auto *ResetCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
+
+ std::string LeftText = clang::Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Left->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ std::string RightText = clang::Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Right->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+
+ if (ResetMember->isArrow())
+ LeftText = "*" + LeftText;
+ if (ReleaseMember->isArrow())
+ RightText = "*" + RightText;
+ std::string DiagText;
+ // Even if x was rvalue, *x is not rvalue anymore.
+ if (!Right->isRValue() || ReleaseMember->isArrow()) {
+ RightText = "std::move(" + RightText + ")";
+ DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
+ } else {
+ DiagText =
+ "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
+ }
+ std::string NewText = LeftText + " = " + RightText;
+
+ diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UniqueptrResetReleaseCheck.h - clang-tidy --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find and replace `unique_ptr::reset(release())` with `std::move()`.
+///
+/// Example:
+///
+/// \code
+/// std::unique_ptr<Foo> x, y;
+/// x.reset(y.release()); -> x = std::move(y);
+/// \endcode
+///
+/// If `y` is already rvalue, `std::move()` is not added. `x` and `y` can also
+/// be `std::unique_ptr<Foo>*`.
+class UniqueptrResetReleaseCheck : public ClangTidyCheck {
+public:
+ UniqueptrResetReleaseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
--- /dev/null
+//===--- UnusedAliasDeclsCheck.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 "UnusedAliasDeclsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UnusedAliasDeclsCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++11; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // We cannot do anything about headers (yet), as the alias declarations
+ // used in one header could be used by some other translation unit.
+ Finder->addMatcher(namespaceAliasDecl(isExpansionInMainFile()).bind("alias"),
+ this);
+ Finder->addMatcher(nestedNameSpecifier().bind("nns"), this);
+}
+
+void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *AliasDecl = Result.Nodes.getNodeAs<NamedDecl>("alias")) {
+ FoundDecls[AliasDecl] = CharSourceRange::getCharRange(
+ AliasDecl->getLocStart(),
+ Lexer::findLocationAfterToken(
+ AliasDecl->getLocEnd(), tok::semi, *Result.SourceManager,
+ getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true));
+ return;
+ }
+
+ if (const auto *NestedName =
+ Result.Nodes.getNodeAs<NestedNameSpecifier>("nns")) {
+ if (const auto *AliasDecl = NestedName->getAsNamespaceAlias()) {
+ FoundDecls[AliasDecl] = CharSourceRange();
+ }
+ }
+}
+
+void UnusedAliasDeclsCheck::onEndOfTranslationUnit() {
+ for (const auto &FoundDecl : FoundDecls) {
+ if (!FoundDecl.second.isValid())
+ continue;
+ diag(FoundDecl.first->getLocation(), "namespace alias decl %0 is unused")
+ << FoundDecl.first << FixItHint::CreateRemoval(FoundDecl.second);
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnusedAliasDeclsCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused namespace alias declarations.
+class UnusedAliasDeclsCheck : public ClangTidyCheck {
+public:
+ UnusedAliasDeclsCheck(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;
+
+private:
+ llvm::DenseMap<const NamedDecl *, CharSourceRange> FoundDecls;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
--- /dev/null
+//===--- UnusedParametersCheck.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 "UnusedParametersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <unordered_set>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+bool isOverrideMethod(const FunctionDecl *Function) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Function))
+ return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+ return false;
+}
+} // namespace
+
+void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(isDefinition(), hasBody(stmt()), hasAnyParameter(decl()))
+ .bind("function"),
+ this);
+}
+
+template <typename T>
+static CharSourceRange removeNode(const MatchFinder::MatchResult &Result,
+ const T *PrevNode, const T *Node,
+ const T *NextNode) {
+ if (NextNode)
+ return CharSourceRange::getCharRange(Node->getLocStart(),
+ NextNode->getLocStart());
+
+ if (PrevNode)
+ return CharSourceRange::getTokenRange(
+ Lexer::getLocForEndOfToken(PrevNode->getLocEnd(), 0,
+ *Result.SourceManager,
+ Result.Context->getLangOpts()),
+ Node->getLocEnd());
+
+ return CharSourceRange::getTokenRange(Node->getSourceRange());
+}
+
+static FixItHint removeParameter(const MatchFinder::MatchResult &Result,
+ const FunctionDecl *Function, unsigned Index) {
+ return FixItHint::CreateRemoval(removeNode(
+ Result, Index > 0 ? Function->getParamDecl(Index - 1) : nullptr,
+ Function->getParamDecl(Index),
+ Index + 1 < Function->getNumParams() ? Function->getParamDecl(Index + 1)
+ : nullptr));
+}
+
+static FixItHint removeArgument(const MatchFinder::MatchResult &Result,
+ const CallExpr *Call, unsigned Index) {
+ return FixItHint::CreateRemoval(removeNode(
+ Result, Index > 0 ? Call->getArg(Index - 1) : nullptr,
+ Call->getArg(Index),
+ Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));
+}
+
+class UnusedParametersCheck::IndexerVisitor
+ : public RecursiveASTVisitor<IndexerVisitor> {
+public:
+ IndexerVisitor(TranslationUnitDecl *Top) { TraverseDecl(Top); }
+
+ const std::unordered_set<const CallExpr *> &
+ getFnCalls(const FunctionDecl *Fn) {
+ return Index[Fn->getCanonicalDecl()].Calls;
+ }
+
+ const std::unordered_set<const DeclRefExpr *> &
+ getOtherRefs(const FunctionDecl *Fn) {
+ return Index[Fn->getCanonicalDecl()].OtherRefs;
+ }
+
+ bool shouldTraversePostOrder() const { return true; }
+
+ bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {
+ if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
+ Fn = Fn->getCanonicalDecl();
+ Index[Fn].OtherRefs.insert(DeclRef);
+ }
+ return true;
+ }
+
+ bool WalkUpFromCallExpr(CallExpr *Call) {
+ if (const auto *Fn =
+ dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
+ Fn = Fn->getCanonicalDecl();
+ if (const auto *Ref =
+ dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {
+ Index[Fn].OtherRefs.erase(Ref);
+ }
+ Index[Fn].Calls.insert(Call);
+ }
+ return true;
+ }
+
+private:
+ struct IndexEntry {
+ std::unordered_set<const CallExpr *> Calls;
+ std::unordered_set<const DeclRefExpr *> OtherRefs;
+ };
+
+ std::unordered_map<const FunctionDecl *, IndexEntry> Index;
+};
+
+UnusedParametersCheck::~UnusedParametersCheck() = default;
+
+UnusedParametersCheck::UnusedParametersCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void UnusedParametersCheck::warnOnUnusedParameter(
+ const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
+ unsigned ParamIndex) {
+ const auto *Param = Function->getParamDecl(ParamIndex);
+ auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;
+
+ if (!Indexer) {
+ Indexer = llvm::make_unique<IndexerVisitor>(
+ Result.Context->getTranslationUnitDecl());
+ }
+
+ // Comment out parameter name for non-local functions.
+ if (Function->isExternallyVisible() ||
+ !Result.SourceManager->isInMainFile(Function->getLocation()) ||
+ !Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function)) {
+ SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
+ // Note: We always add a space before the '/*' to not accidentally create a
+ // '*/*' for pointer types, which doesn't start a comment. clang-format will
+ // clean this up afterwards.
+ MyDiag << FixItHint::CreateReplacement(
+ RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
+ return;
+ }
+
+ // Fix all redeclarations.
+ for (const FunctionDecl *FD : Function->redecls())
+ if (FD->param_size())
+ MyDiag << removeParameter(Result, FD, ParamIndex);
+
+ // Fix all call sites.
+ for (const auto *Call : Indexer->getFnCalls(Function))
+ MyDiag << removeArgument(Result, Call, ParamIndex);
+}
+
+void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
+ if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
+ return;
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(Function))
+ if (Method->isLambdaStaticInvoker())
+ return;
+ for (unsigned i = 0, e = Function->getNumParams(); i != e; ++i) {
+ const auto *Param = Function->getParamDecl(i);
+ if (Param->isUsed() || Param->isReferenced() || !Param->getDeclName() ||
+ Param->hasAttr<UnusedAttr>())
+ continue;
+ warnOnUnusedParameter(Result, Function, i);
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnusedParametersCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused parameters and fixes them, so that `-Wunused-parameter` can be
+/// turned on.
+class UnusedParametersCheck : public ClangTidyCheck {
+public:
+ UnusedParametersCheck(StringRef Name, ClangTidyContext *Context);
+ ~UnusedParametersCheck();
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ class IndexerVisitor;
+ std::unique_ptr<IndexerVisitor> Indexer;
+
+ void
+ warnOnUnusedParameter(const ast_matchers::MatchFinder::MatchResult &Result,
+ const FunctionDecl *Function, unsigned ParamIndex);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
--- /dev/null
+//===--- UnusedRAIICheck.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 "UnusedRAIICheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(CXXRecordDecl, hasNonTrivialDestructor) {
+ // TODO: If the dtor is there but empty we don't want to warn either.
+ return Node.hasDefinition() && Node.hasNonTrivialDestructor();
+}
+} // namespace
+
+void UnusedRAIICheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Look for temporaries that are constructed in-place and immediately
+ // destroyed. Look for temporaries created by a functional cast but not for
+ // those returned from a call.
+ auto BindTemp =
+ cxxBindTemporaryExpr(unless(has(ignoringParenImpCasts(callExpr()))))
+ .bind("temp");
+ Finder->addMatcher(
+ exprWithCleanups(unless(isInTemplateInstantiation()),
+ hasParent(compoundStmt().bind("compound")),
+ hasType(cxxRecordDecl(hasNonTrivialDestructor())),
+ anyOf(has(ignoringParenImpCasts(BindTemp)),
+ has(ignoringParenImpCasts(cxxFunctionalCastExpr(
+ has(ignoringParenImpCasts(BindTemp)))))))
+ .bind("expr"),
+ this);
+}
+
+void UnusedRAIICheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+
+ // We ignore code expanded from macros to reduce the number of false
+ // positives.
+ if (E->getLocStart().isMacroID())
+ return;
+
+ // Don't emit a warning for the last statement in the surrounding compund
+ // statement.
+ const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
+ if (E == CS->body_back())
+ return;
+
+ // Emit a warning.
+ auto D = diag(E->getLocStart(), "object destroyed immediately after "
+ "creation; did you mean to name the object?");
+ const char *Replacement = " give_me_a_name";
+
+ // If this is a default ctor we have to remove the parens or we'll introduce a
+ // most vexing parse.
+ const auto *BTE = Result.Nodes.getNodeAs<CXXBindTemporaryExpr>("temp");
+ if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr()))
+ if (TOE->getNumArgs() == 0) {
+ D << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()),
+ Replacement);
+ return;
+ }
+
+ // Otherwise just suggest adding a name. To find the place to insert the name
+ // find the first TypeLoc in the children of E, which always points to the
+ // written type.
+ auto Matches =
+ match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context);
+ const auto *TL = selectFirst<TypeLoc>("t", Matches);
+ D << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager,
+ getLangOpts()),
+ Replacement);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnusedRAIICheck.h - clang-tidy -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds temporaries that look like RAII objects.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unused-raii.html
+class UnusedRAIICheck : public ClangTidyCheck {
+public:
+ UnusedRAIICheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
--- /dev/null
+//===--- UnusedUsingDeclsCheck.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 "UnusedUsingDeclsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+// A function that helps to tell whether a TargetDecl in a UsingDecl will be
+// checked. Only variable, function, function template, class template, class,
+// enum declaration and enum constant declaration are considered.
+static bool ShouldCheckDecl(const Decl *TargetDecl) {
+ return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
+ isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
+ isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
+ isa<EnumConstantDecl>(TargetDecl);
+}
+
+void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
+ auto DeclMatcher = hasDeclaration(namedDecl().bind("used"));
+ Finder->addMatcher(loc(enumType(DeclMatcher)), this);
+ Finder->addMatcher(loc(recordType(DeclMatcher)), this);
+ Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this);
+ 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);
+ Finder->addMatcher(loc(templateSpecializationType(hasAnyTemplateArgument(
+ templateArgument().bind("used")))),
+ this);
+}
+
+void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
+ if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+ return;
+
+ if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+ // Ignores using-declarations defined in macros.
+ if (Using->getLocation().isMacroID())
+ return;
+
+ // Ignores using-declarations defined in class definition.
+ if (isa<CXXRecordDecl>(Using->getDeclContext()))
+ return;
+
+ // FIXME: We ignore using-decls defined in function definitions at the
+ // moment because of false positives caused by ADL and different function
+ // scopes.
+ if (isa<FunctionDecl>(Using->getDeclContext()))
+ return;
+
+ UsingDeclContext Context(Using);
+ Context.UsingDeclRange = CharSourceRange::getCharRange(
+ Using->getLocStart(),
+ Lexer::findLocationAfterToken(
+ Using->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true));
+ for (const auto *UsingShadow : Using->shadows()) {
+ const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
+ if (ShouldCheckDecl(TargetDecl))
+ Context.UsingTargetDecls.insert(TargetDecl);
+ }
+ if (!Context.UsingTargetDecls.empty())
+ 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<NamedDecl>("used")) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
+ removeFromFoundDecls(FD->getPrimaryTemplate());
+ } else if (const auto *Specialization =
+ dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
+ Used = Specialization->getSpecializedTemplate();
+ }
+ removeFromFoundDecls(Used);
+ return;
+ }
+
+ if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
+ // FIXME: Support non-type template parameters.
+ if (Used->getKind() == TemplateArgument::Template) {
+ if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
+ removeFromFoundDecls(TD);
+ } else if (Used->getKind() == TemplateArgument::Type) {
+ if (auto *RD = Used->getAsType()->getAsCXXRecordDecl())
+ removeFromFoundDecls(RD);
+ }
+ return;
+ }
+
+ if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) {
+ removeFromFoundDecls(Used->getAsTemplateDecl());
+ return;
+ }
+
+ if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+ if (const auto *FDT = FD->getPrimaryTemplate())
+ removeFromFoundDecls(FDT);
+ else
+ removeFromFoundDecls(FD);
+ } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+ removeFromFoundDecls(VD);
+ } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
+ removeFromFoundDecls(ECD);
+ if (const auto *ET = ECD->getType()->getAs<EnumType>())
+ removeFromFoundDecls(ET->getDecl());
+ }
+ }
+ // Check the uninstantiated template function usage.
+ if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) {
+ for (const NamedDecl *ND : ULE->decls()) {
+ if (const auto *USD = dyn_cast<UsingShadowDecl>(ND))
+ removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
+ }
+ }
+}
+
+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.
+ //
+ // FIXME: Use a more efficient way to find a matching context.
+ for (auto &Context : Contexts) {
+ if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0)
+ Context.IsUsed = true;
+ }
+}
+
+void UnusedUsingDeclsCheck::onEndOfTranslationUnit() {
+ for (const auto &Context : Contexts) {
+ if (!Context.IsUsed) {
+ diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused")
+ << Context.FoundUsingDecl
+ << FixItHint::CreateRemoval(Context.UsingDeclRange);
+ }
+ }
+ Contexts.clear();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnusedUsingDeclsCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused using declarations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unused-using-decls.html
+class UnusedUsingDeclsCheck : public ClangTidyCheck {
+public:
+ UnusedUsingDeclsCheck(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;
+
+private:
+ void removeFromFoundDecls(const Decl *D);
+
+ struct UsingDeclContext {
+ explicit UsingDeclContext(const UsingDecl *FoundUsingDecl)
+ : FoundUsingDecl(FoundUsingDecl), IsUsed(false) {}
+ // A set saves all UsingShadowDecls introduced by a UsingDecl. A UsingDecl
+ // can introduce multiple UsingShadowDecls in some cases (such as
+ // overloaded functions).
+ llvm::SmallPtrSet<const Decl *, 4> UsingTargetDecls;
+ // The original UsingDecl.
+ const UsingDecl *FoundUsingDecl;
+ // The source range of the UsingDecl.
+ CharSourceRange UsingDeclRange;
+ // Whether the UsingDecl is used.
+ bool IsUsed;
+ };
+
+ std::vector<UsingDeclContext> Contexts;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
--- /dev/null
+//===--- UseAfterMoveCheck.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 "UseAfterMoveCheck.h"
+
+#include "clang/Analysis/CFG.h"
+#include "clang/Lex/Lexer.h"
+
+#include "../utils/ExprSequence.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils;
+
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+/// Contains information about a use-after-move.
+struct UseAfterMove {
+ // The DeclRefExpr that constituted the use of the object.
+ const DeclRefExpr *DeclRef;
+
+ // Is the order in which the move and the use are evaluated undefined?
+ bool EvaluationOrderUndefined;
+};
+
+/// Finds uses of a variable after a move (and maintains state required by the
+/// various internal helper functions).
+class UseAfterMoveFinder {
+public:
+ UseAfterMoveFinder(ASTContext *TheContext);
+
+ // Within the given function body, finds the first use of 'MovedVariable' that
+ // occurs after 'MovingCall' (the expression that performs the move). If a
+ // use-after-move is found, writes information about it to 'TheUseAfterMove'.
+ // Returns whether a use-after-move was found.
+ bool find(Stmt *FunctionBody, const Expr *MovingCall,
+ const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+
+private:
+ bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove);
+ void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits);
+ void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+ void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+
+ ASTContext *Context;
+ std::unique_ptr<ExprSequence> Sequence;
+ std::unique_ptr<StmtToBlockMap> BlockMap;
+ llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
+};
+
+} // namespace
+
+
+// Matches nodes that are
+// - Part of a decltype argument or class template argument (we check this by
+// seeing if they are children of a TypeLoc), or
+// - Part of a function template argument (we check this by seeing if they are
+// children of a DeclRefExpr that references a function template).
+// DeclRefExprs that fulfill these conditions should not be counted as a use or
+// move.
+static StatementMatcher inDecltypeOrTemplateArg() {
+ return anyOf(hasAncestor(typeLoc()),
+ hasAncestor(declRefExpr(
+ to(functionDecl(ast_matchers::isTemplateInstantiation())))));
+}
+
+UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
+ : Context(TheContext) {}
+
+bool UseAfterMoveFinder::find(Stmt *FunctionBody, const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove) {
+ // Generate the CFG manually instead of through an AnalysisDeclContext because
+ // it seems the latter can't be used to generate a CFG for the body of a
+ // labmda.
+ //
+ // We include implicit and temporary destructors in the CFG so that
+ // destructors marked [[noreturn]] are handled correctly in the control flow
+ // analysis. (These are used in some styles of assertion macros.)
+ CFG::BuildOptions Options;
+ Options.AddImplicitDtors = true;
+ Options.AddTemporaryDtors = true;
+ std::unique_ptr<CFG> TheCFG =
+ CFG::buildCFG(nullptr, FunctionBody, Context, Options);
+ if (!TheCFG)
+ return false;
+
+ Sequence.reset(new ExprSequence(TheCFG.get(), Context));
+ BlockMap.reset(new StmtToBlockMap(TheCFG.get(), Context));
+ Visited.clear();
+
+ const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
+ if (!Block)
+ return false;
+
+ return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+}
+
+bool UseAfterMoveFinder::findInternal(const CFGBlock *Block,
+ const Expr *MovingCall,
+ const ValueDecl *MovedVariable,
+ UseAfterMove *TheUseAfterMove) {
+ if (Visited.count(Block))
+ return false;
+
+ // Mark the block as visited (except if this is the block containing the
+ // std::move() and it's being visited the first time).
+ if (!MovingCall)
+ Visited.insert(Block);
+
+ // Get all uses and reinits in the block.
+ llvm::SmallVector<const DeclRefExpr *, 1> Uses;
+ llvm::SmallPtrSet<const Stmt *, 1> Reinits;
+ getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);
+
+ // Ignore all reinitializations where the move potentially comes after the
+ // reinit.
+ llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;
+ for (const Stmt *Reinit : Reinits) {
+ if (MovingCall && Sequence->potentiallyAfter(MovingCall, Reinit))
+ ReinitsToDelete.push_back(Reinit);
+ }
+ for (const Stmt *Reinit : ReinitsToDelete) {
+ Reinits.erase(Reinit);
+ }
+
+ // Find all uses that potentially come after the move.
+ for (const DeclRefExpr *Use : Uses) {
+ if (!MovingCall || Sequence->potentiallyAfter(Use, MovingCall)) {
+ // Does the use have a saving reinit? A reinit is saving if it definitely
+ // comes before the use, i.e. if there's no potential that the reinit is
+ // after the use.
+ bool HaveSavingReinit = false;
+ for (const Stmt *Reinit : Reinits) {
+ if (!Sequence->potentiallyAfter(Reinit, Use))
+ HaveSavingReinit = true;
+ }
+
+ if (!HaveSavingReinit) {
+ TheUseAfterMove->DeclRef = Use;
+
+ // Is this a use-after-move that depends on order of evaluation?
+ // This is the case if the move potentially comes after the use (and we
+ // already know that use potentially comes after the move, which taken
+ // together tells us that the ordering is unclear).
+ TheUseAfterMove->EvaluationOrderUndefined =
+ MovingCall != nullptr &&
+ Sequence->potentiallyAfter(MovingCall, Use);
+
+ return true;
+ }
+ }
+ }
+
+ // If the object wasn't reinitialized, call ourselves recursively on all
+ // successors.
+ if (Reinits.empty()) {
+ for (const auto &Succ : Block->succs()) {
+ if (Succ && findInternal(Succ, nullptr, MovedVariable, TheUseAfterMove))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void UseAfterMoveFinder::getUsesAndReinits(
+ const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+ llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;
+ llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;
+
+ getDeclRefs(Block, MovedVariable, &DeclRefs);
+ getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
+
+ // All references to the variable that aren't reinitializations are uses.
+ Uses->clear();
+ for (const DeclRefExpr *DeclRef : DeclRefs) {
+ if (!ReinitDeclRefs.count(DeclRef))
+ Uses->push_back(DeclRef);
+ }
+
+ // Sort the uses by their occurrence in the source code.
+ std::sort(Uses->begin(), Uses->end(),
+ [](const DeclRefExpr *D1, const DeclRefExpr *D2) {
+ return D1->getExprLoc() < D2->getExprLoc();
+ });
+}
+
+bool isStandardSmartPointer(const ValueDecl *VD) {
+ const Type *TheType = VD->getType().getTypePtrOrNull();
+ if (!TheType)
+ return false;
+
+ const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
+ if (!RecordDecl)
+ return false;
+
+ const IdentifierInfo *ID = RecordDecl->getIdentifier();
+ if (!ID)
+ return false;
+
+ StringRef Name = ID->getName();
+ if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
+ return false;
+
+ return RecordDecl->getDeclContext()->isStdNamespace();
+}
+
+void UseAfterMoveFinder::getDeclRefs(
+ const CFGBlock *Block, const Decl *MovedVariable,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+ DeclRefs->clear();
+ for (const auto &Elem : *Block) {
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+ if (!S)
+ continue;
+
+ auto addDeclRefs = [this, Block,
+ DeclRefs](const ArrayRef<BoundNodes> Matches) {
+ for (const auto &Match : Matches) {
+ const auto *DeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+ const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>("operator");
+ if (DeclRef && BlockMap->blockContainingStmt(DeclRef) == Block) {
+ // Ignore uses of a standard smart pointer that don't dereference the
+ // pointer.
+ if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
+ DeclRefs->insert(DeclRef);
+ }
+ }
+ }
+ };
+
+ auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
+ unless(inDecltypeOrTemplateArg()))
+ .bind("declref");
+
+ addDeclRefs(match(findAll(DeclRefMatcher), *S->getStmt(), *Context));
+ addDeclRefs(match(
+ findAll(cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("*"),
+ hasOverloadedOperatorName("->"),
+ hasOverloadedOperatorName("[]")),
+ hasArgument(0, DeclRefMatcher))
+ .bind("operator")),
+ *S->getStmt(), *Context));
+ }
+}
+
+void UseAfterMoveFinder::getReinits(
+ const CFGBlock *Block, const ValueDecl *MovedVariable,
+ llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+ llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+ auto DeclRefMatcher =
+ declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind("declref");
+
+ auto StandardContainerTypeMatcher = hasType(cxxRecordDecl(
+ hasAnyName("::std::basic_string", "::std::vector", "::std::deque",
+ "::std::forward_list", "::std::list", "::std::set",
+ "::std::map", "::std::multiset", "::std::multimap",
+ "::std::unordered_set", "::std::unordered_map",
+ "::std::unordered_multiset", "::std::unordered_multimap")));
+
+ auto StandardSmartPointerTypeMatcher = hasType(cxxRecordDecl(
+ hasAnyName("::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr")));
+
+ // Matches different types of reinitialization.
+ auto ReinitMatcher =
+ stmt(anyOf(
+ // Assignment. In addition to the overloaded assignment operator,
+ // test for built-in assignment as well, since template functions
+ // may be instantiated to use std::move() on built-in types.
+ binaryOperator(hasOperatorName("="), hasLHS(DeclRefMatcher)),
+ cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+ hasArgument(0, DeclRefMatcher)),
+ // Declaration. We treat this as a type of reinitialization too,
+ // so we don't need to treat it separately.
+ declStmt(hasDescendant(equalsNode(MovedVariable))),
+ // clear() and assign() on standard containers.
+ cxxMemberCallExpr(
+ on(allOf(DeclRefMatcher, StandardContainerTypeMatcher)),
+ // To keep the matcher simple, we check for assign() calls
+ // on all standard containers, even though only vector,
+ // deque, forward_list and list have assign(). If assign()
+ // is called on any of the other containers, this will be
+ // flagged by a compile error anyway.
+ callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
+ // reset() on standard smart pointers.
+ cxxMemberCallExpr(
+ on(allOf(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
+ callee(cxxMethodDecl(hasName("reset")))),
+ // Passing variable to a function as a non-const pointer.
+ callExpr(forEachArgumentWithParam(
+ unaryOperator(hasOperatorName("&"),
+ hasUnaryOperand(DeclRefMatcher)),
+ unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
+ // Passing variable to a function as a non-const lvalue reference
+ // (unless that function is std::move()).
+ callExpr(forEachArgumentWithParam(
+ DeclRefMatcher,
+ unless(parmVarDecl(hasType(
+ references(qualType(isConstQualified())))))),
+ unless(callee(functionDecl(hasName("::std::move")))))))
+ .bind("reinit");
+
+ Stmts->clear();
+ DeclRefs->clear();
+ for (const auto &Elem : *Block) {
+ Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+ if (!S)
+ continue;
+
+ SmallVector<BoundNodes, 1> Matches =
+ match(findAll(ReinitMatcher), *S->getStmt(), *Context);
+
+ for (const auto &Match : Matches) {
+ const auto *TheStmt = Match.getNodeAs<Stmt>("reinit");
+ const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+ if (TheStmt && BlockMap->blockContainingStmt(TheStmt) == Block) {
+ Stmts->insert(TheStmt);
+
+ // We count DeclStmts as reinitializations, but they don't have a
+ // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'
+ // before adding it to the set.
+ if (TheDeclRef)
+ DeclRefs->insert(TheDeclRef);
+ }
+ }
+ }
+}
+
+static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg,
+ const UseAfterMove &Use, ClangTidyCheck *Check,
+ ASTContext *Context) {
+ SourceLocation UseLoc = Use.DeclRef->getExprLoc();
+ SourceLocation MoveLoc = MovingCall->getExprLoc();
+
+ Check->diag(UseLoc, "'%0' used after it was moved")
+ << MoveArg->getDecl()->getName();
+ Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note);
+ if (Use.EvaluationOrderUndefined) {
+ Check->diag(UseLoc,
+ "the use and move are unsequenced, i.e. there is no guarantee "
+ "about the order in which they are evaluated",
+ DiagnosticIDs::Note);
+ } else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
+ Check->diag(UseLoc,
+ "the use happens in a later loop iteration than the move",
+ DiagnosticIDs::Note);
+ }
+}
+
+void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ auto CallMoveMatcher =
+ callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
+ hasArgument(0, declRefExpr().bind("arg")),
+ anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
+ hasAncestor(functionDecl().bind("containing-func"))),
+ unless(inDecltypeOrTemplateArg()))
+ .bind("call-move");
+
+ Finder->addMatcher(
+ // To find the Stmt that we assume performs the actual move, we look for
+ // the direct ancestor of the std::move() that isn't one of the node
+ // types ignored by ignoringParenImpCasts().
+ stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
+ // Don't allow an InitListExpr to be the moving call. An InitListExpr
+ // has both a syntactic and a semantic form, and the parent-child
+ // relationships are different between the two. This could cause an
+ // InitListExpr to be analyzed as the moving call in addition to the
+ // Expr that we actually want, resulting in two diagnostics with
+ // different code locations for the same move.
+ unless(initListExpr()),
+ unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
+ .bind("moving-call"),
+ this);
+}
+
+void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ContainingLambda =
+ Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
+ const auto *ContainingFunc =
+ Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
+ const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+ const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
+ const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
+
+ if (!MovingCall || !MovingCall->getExprLoc().isValid())
+ MovingCall = CallMove;
+
+ Stmt *FunctionBody = nullptr;
+ if (ContainingLambda)
+ FunctionBody = ContainingLambda->getBody();
+ else if (ContainingFunc)
+ FunctionBody = ContainingFunc->getBody();
+ else
+ return;
+
+ // Ignore the std::move if the variable that was passed to it isn't a local
+ // variable.
+ if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
+ return;
+
+ UseAfterMoveFinder finder(Result.Context);
+ UseAfterMove Use;
+ if (finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use))
+ emitDiagnostic(MovingCall, Arg, Use, this, Result.Context);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseAfterMoveCheck.h - clang-tidy ---------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check warns if an object is used after it has been moved, without an
+/// intervening reinitialization.
+///
+/// For details, see the user-facing documentation:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-use-after-move.html
+class UseAfterMoveCheck : public ClangTidyCheck {
+public:
+ UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
--- /dev/null
+//===--- VirtualNearMissCheck.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 "VirtualNearMissCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
+
+AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
+ return Node.isOverloadedOperator();
+}
+
+/// Finds out if the given method overrides some method.
+static bool isOverrideMethod(const CXXMethodDecl *MD) {
+ return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+}
+
+/// Checks whether the return types are covariant, according to
+/// C++[class.virtual]p7.
+///
+/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
+/// \returns true if the return types of BaseMD and DerivedMD are covariant.
+static bool checkOverridingFunctionReturnType(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ QualType BaseReturnTy = BaseMD->getType()
+ ->getAs<FunctionType>()
+ ->getReturnType()
+ .getCanonicalType();
+ QualType DerivedReturnTy = DerivedMD->getType()
+ ->getAs<FunctionType>()
+ ->getReturnType()
+ .getCanonicalType();
+
+ if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
+ return false;
+
+ // Check if return types are identical.
+ if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
+ return true;
+
+ /// Check if the return types are covariant.
+
+ // Both types must be pointers or references to classes.
+ if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
+ !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
+ return false;
+
+ /// BTy is the class type in return type of BaseMD. For example,
+ /// B* Base::md()
+ /// While BRD is the declaration of B.
+ QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
+ QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
+
+ const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
+ const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
+ if (DRD == nullptr || BRD == nullptr)
+ return false;
+
+ if (!DRD->hasDefinition() || !BRD->hasDefinition())
+ return false;
+
+ if (DRD == BRD)
+ return true;
+
+ if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
+ // Begin checking whether the conversion from D to B is valid.
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+
+ // Check whether D is derived from B, and fill in a CXXBasePaths object.
+ if (!DRD->isDerivedFrom(BRD, Paths))
+ return false;
+
+ // Check ambiguity.
+ if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
+ return false;
+
+ // Check accessibility.
+ // FIXME: We currently only support checking if B is accessible base class
+ // of D, or D is the same class which DerivedMD is in.
+ bool IsItself =
+ DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
+ bool HasPublicAccess = false;
+ for (const auto &Path : Paths) {
+ if (Path.Access == AS_public)
+ HasPublicAccess = true;
+ }
+ if (!HasPublicAccess && !IsItself)
+ return false;
+ // End checking conversion from D to B.
+ }
+
+ // Both pointers or references should have the same cv-qualification.
+ if (DerivedReturnTy.getLocalCVRQualifiers() !=
+ BaseReturnTy.getLocalCVRQualifiers())
+ return false;
+
+ // The class type D should have the same cv-qualification as or less
+ // cv-qualification than the class type B.
+ if (DTy.isMoreQualifiedThan(BTy))
+ return false;
+
+ return true;
+}
+
+/// \returns decayed type for arrays and functions.
+static QualType getDecayedType(QualType Type) {
+ if (const auto *Decayed = Type->getAs<DecayedType>())
+ return Decayed->getDecayedType();
+ return Type;
+}
+
+/// \returns true if the param types are the same.
+static bool checkParamTypes(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ unsigned NumParamA = BaseMD->getNumParams();
+ unsigned NumParamB = DerivedMD->getNumParams();
+ if (NumParamA != NumParamB)
+ return false;
+
+ for (unsigned I = 0; I < NumParamA; I++) {
+ if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
+ getDecayedType(
+ DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
+ return false;
+ }
+ return true;
+}
+
+/// \returns true if derived method can override base method except for the
+/// name.
+static bool checkOverrideWithoutName(const ASTContext *Context,
+ const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ if (BaseMD->isStatic() != DerivedMD->isStatic())
+ return false;
+
+ if (BaseMD->getType() == DerivedMD->getType())
+ return true;
+
+ // Now the function types are not identical. Then check if the return types
+ // are covariant and if the param types are the same.
+ if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
+ return false;
+ return checkParamTypes(BaseMD, DerivedMD);
+}
+
+/// Check whether BaseMD overrides DerivedMD.
+///
+/// Prerequisite: the class which BaseMD is in should be a base class of that
+/// DerivedMD is in.
+static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
+ const CXXMethodDecl *DerivedMD) {
+ for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
+ E = DerivedMD->end_overridden_methods();
+ I != E; ++I) {
+ const CXXMethodDecl *OverriddenMD = *I;
+ if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
+ return true;
+ }
+
+ return false;
+}
+
+bool VirtualNearMissCheck::isPossibleToBeOverridden(
+ const CXXMethodDecl *BaseMD) {
+ auto Iter = PossibleMap.find(BaseMD);
+ if (Iter != PossibleMap.end())
+ return Iter->second;
+
+ bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
+ !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
+ !BaseMD->isOverloadedOperator() &&
+ !isa<CXXConversionDecl>(BaseMD);
+ PossibleMap[BaseMD] = IsPossible;
+ return IsPossible;
+}
+
+bool VirtualNearMissCheck::isOverriddenByDerivedClass(
+ const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
+ auto Key = std::make_pair(BaseMD, DerivedRD);
+ auto Iter = OverriddenMap.find(Key);
+ if (Iter != OverriddenMap.end())
+ return Iter->second;
+
+ bool IsOverridden = false;
+ for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
+ if (!isOverrideMethod(DerivedMD))
+ continue;
+
+ if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
+ IsOverridden = true;
+ break;
+ }
+ }
+ OverriddenMap[Key] = IsOverridden;
+ return IsOverridden;
+}
+
+void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cxxMethodDecl(
+ unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
+ cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
+ isOverloadedOperator())))
+ .bind("method"),
+ this);
+}
+
+void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+ assert(DerivedMD);
+
+ const ASTContext *Context = Result.Context;
+
+ const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
+ assert(DerivedRD);
+
+ for (const auto &BaseSpec : DerivedRD->bases()) {
+ if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
+ for (const auto *BaseMD : BaseRD->methods()) {
+ if (!isPossibleToBeOverridden(BaseMD))
+ continue;
+
+ if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
+ continue;
+
+ unsigned EditDistance =
+ BaseMD->getName().edit_distance(DerivedMD->getName());
+ if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
+ if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
+ // A "virtual near miss" is found.
+ auto Range = CharSourceRange::getTokenRange(
+ SourceRange(DerivedMD->getLocation()));
+
+ bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
+ !DerivedMD->isTemplateInstantiation();
+ auto Diag =
+ diag(DerivedMD->getLocStart(),
+ "method '%0' has a similar name and the same signature as "
+ "virtual method '%1'; did you mean to override it?")
+ << DerivedMD->getQualifiedNameAsString()
+ << BaseMD->getQualifiedNameAsString();
+ if (ApplyFix)
+ Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- VirtualNearMissCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
+
+#include "../ClangTidy.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Checks for near miss of virtual methods.
+///
+/// For a method in a derived class, this check looks for virtual method with a
+/// very similar name and an identical signature defined in a base class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-virtual-near-miss.html
+class VirtualNearMissCheck : public ClangTidyCheck {
+public:
+ VirtualNearMissCheck(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 given method is possible to be overridden by some other
+ /// method. Operators and destructors are excluded.
+ ///
+ /// Results are memoized in PossibleMap.
+ bool isPossibleToBeOverridden(const CXXMethodDecl *BaseMD);
+
+ /// Check if the given base method is overridden by some methods in the given
+ /// derived class.
+ ///
+ /// Results are memoized in OverriddenMap.
+ bool isOverriddenByDerivedClass(const CXXMethodDecl *BaseMD,
+ const CXXRecordDecl *DerivedRD);
+
+ /// Key: the unique ID of a method.
+ /// Value: whether the method is possible to be overridden.
+ std::map<const CXXMethodDecl *, bool> PossibleMap;
+
+ /// Key: <unique ID of base method, name of derived class>
+ /// Value: whether the base method is overridden by some method in the derived
+ /// class.
+ std::map<std::pair<const CXXMethodDecl *, const CXXRecordDecl *>, bool>
+ OverriddenMap;
+
+ const unsigned EditDistanceThreshold = 1;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
--- /dev/null
+//===--- AvoidBindCheck.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 "AvoidBindCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstddef>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
+
+struct BindArgument {
+ StringRef Tokens;
+ BindArgumentKind Kind = BK_Other;
+ size_t PlaceHolderIndex = 0;
+};
+
+} // end namespace
+
+static SmallVector<BindArgument, 4>
+buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) {
+ SmallVector<BindArgument, 4> BindArguments;
+ llvm::Regex MatchPlaceholder("^_([0-9]+)$");
+
+ // Start at index 1 as first argument to bind is the function name.
+ for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
+ const Expr *E = C->getArg(I);
+ BindArgument B;
+ if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
+ const auto *TE = M->GetTemporaryExpr();
+ B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
+ }
+
+ B.Tokens = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ SmallVector<StringRef, 2> Matches;
+ if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
+ B.Kind = BK_Placeholder;
+ B.PlaceHolderIndex = std::stoi(Matches[1]);
+ }
+ BindArguments.push_back(B);
+ }
+ return BindArguments;
+}
+
+static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
+ llvm::raw_ostream &Stream) {
+ auto MaxPlaceholderIt =
+ std::max_element(Args.begin(), Args.end(),
+ [](const BindArgument &B1, const BindArgument &B2) {
+ return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
+ });
+
+ // Placeholders (if present) have index 1 or greater.
+ if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
+ return;
+
+ size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
+ Stream << "(";
+ StringRef Delimiter = "";
+ for (size_t I = 1; I <= PlaceholderCount; ++I) {
+ Stream << Delimiter << "auto && arg" << I;
+ Delimiter = ", ";
+ }
+ Stream << ")";
+}
+
+static void addFunctionCallArgs(const ArrayRef<BindArgument> Args,
+ llvm::raw_ostream &Stream) {
+ StringRef Delimiter = "";
+ for (const auto &B : Args) {
+ if (B.PlaceHolderIndex)
+ Stream << Delimiter << "arg" << B.PlaceHolderIndex;
+ else
+ Stream << Delimiter << B.Tokens;
+ Delimiter = ", ";
+ }
+}
+
+static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
+ llvm::SmallSet<size_t, 4> PlaceHolderIndices;
+ for (const BindArgument &B : Args) {
+ if (B.PlaceHolderIndex) {
+ if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
+ return true;
+ }
+ }
+ return false;
+}
+
+void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
+ return;
+
+ Finder->addMatcher(
+ callExpr(
+ callee(namedDecl(hasName("::std::bind"))),
+ hasArgument(0, declRefExpr(to(functionDecl().bind("f"))).bind("ref")))
+ .bind("bind"),
+ this);
+}
+
+void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
+ auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind");
+
+ const auto Args = buildBindArguments(Result, MatchedDecl);
+
+ // Do not attempt to create fixits for nested call expressions.
+ // FIXME: Create lambda capture variables to capture output of calls.
+ // NOTE: Supporting nested std::bind will be more difficult due to placeholder
+ // sharing between outer and inner std:bind invocations.
+ if (llvm::any_of(Args,
+ [](const BindArgument &B) { return B.Kind == BK_CallExpr; }))
+ return;
+
+ // Do not attempt to create fixits when placeholders are reused.
+ // Unused placeholders are supported by requiring C++14 generic lambdas.
+ // FIXME: Support this case by deducing the common type.
+ if (isPlaceHolderIndexRepeated(Args))
+ return;
+
+ const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("f");
+
+ // std::bind can support argument count mismatch between its arguments and the
+ // bound function's arguments. Do not attempt to generate a fixit for such
+ // cases.
+ // FIXME: Support this case by creating unused lambda capture variables.
+ if (F->getNumParams() != Args.size())
+ return;
+
+ std::string Buffer;
+ llvm::raw_string_ostream Stream(Buffer);
+
+ bool HasCapturedArgument = llvm::any_of(
+ Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
+ const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
+ Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
+ addPlaceholderArgs(Args, Stream);
+ Stream << " { return ";
+ Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
+ Stream << "(";
+ addFunctionCallArgs(Args, Stream);
+ Stream << "); };";
+
+ Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
+ Stream.str());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- AvoidBindCheck.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_MODERNIZE_AVOID_BIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace simple uses of std::bind with a lambda.
+///
+/// FIXME: Add support for function references and member function references.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-avoid-std-bind.html
+class AvoidBindCheck : public ClangTidyCheck {
+public:
+ AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyModernizeModule
+ AvoidBindCheck.cpp
+ DeprecatedHeadersCheck.cpp
+ LoopConvertCheck.cpp
+ LoopConvertUtils.cpp
+ MakeSmartPtrCheck.cpp
+ MakeSharedCheck.cpp
+ MakeUniqueCheck.cpp
+ ModernizeTidyModule.cpp
+ PassByValueCheck.cpp
+ RawStringLiteralCheck.cpp
+ RedundantVoidArgCheck.cpp
+ ReplaceAutoPtrCheck.cpp
+ ReplaceRandomShuffleCheck.cpp
+ ReturnBracedInitListCheck.cpp
+ ShrinkToFitCheck.cpp
+ UnaryStaticAssertCheck.cpp
+ UseAutoCheck.cpp
+ UseBoolLiteralsCheck.cpp
+ UseDefaultMemberInitCheck.cpp
+ UseEmplaceCheck.cpp
+ UseEqualsDefaultCheck.cpp
+ UseEqualsDeleteCheck.cpp
+ UseNoexceptCheck.cpp
+ UseNullptrCheck.cpp
+ UseOverrideCheck.cpp
+ UseTransparentFunctorsCheck.cpp
+ UseUsingCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyReadabilityModule
+ clangTidyUtils
+ )
--- /dev/null
+//===--- DeprecatedHeadersCheck.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 "DeprecatedHeadersCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSet.h"
+
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+class IncludeModernizePPCallbacks : public PPCallbacks {
+public:
+ explicit IncludeModernizePPCallbacks(ClangTidyCheck &Check,
+ LangOptions LangOpts);
+
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath,
+ const Module *Imported) override;
+
+private:
+ ClangTidyCheck &Check;
+ LangOptions LangOpts;
+ llvm::StringMap<std::string> CStyledHeaderToCxx;
+ llvm::StringSet<> DeleteHeaders;
+};
+} // namespace
+
+void DeprecatedHeadersCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ if (this->getLangOpts().CPlusPlus) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ ::llvm::make_unique<IncludeModernizePPCallbacks>(*this,
+ this->getLangOpts()));
+ }
+}
+
+IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(ClangTidyCheck &Check,
+ LangOptions LangOpts)
+ : Check(Check), LangOpts(LangOpts) {
+ for (const auto &KeyValue :
+ std::vector<std::pair<llvm::StringRef, std::string>>(
+ {{"assert.h", "cassert"},
+ {"complex.h", "complex"},
+ {"ctype.h", "cctype"},
+ {"errno.h", "cerrno"},
+ {"float.h", "cfloat"},
+ {"limits.h", "climits"},
+ {"locale.h", "clocale"},
+ {"math.h", "cmath"},
+ {"setjmp.h", "csetjmp"},
+ {"signal.h", "csignal"},
+ {"stdarg.h", "cstdarg"},
+ {"stddef.h", "cstddef"},
+ {"stdio.h", "cstdio"},
+ {"stdlib.h", "cstdlib"},
+ {"string.h", "cstring"},
+ {"time.h", "ctime"},
+ {"wchar.h", "cwchar"},
+ {"wctype.h", "cwctype"}})) {
+ CStyledHeaderToCxx.insert(KeyValue);
+ }
+ // Add C++ 11 headers.
+ if (LangOpts.CPlusPlus11) {
+ for (const auto &KeyValue :
+ std::vector<std::pair<llvm::StringRef, std::string>>(
+ {{"fenv.h", "cfenv"},
+ {"stdint.h", "cstdint"},
+ {"inttypes.h", "cinttypes"},
+ {"tgmath.h", "ctgmath"},
+ {"uchar.h", "cuchar"}})) {
+ CStyledHeaderToCxx.insert(KeyValue);
+ }
+ }
+ for (const auto &Key :
+ std::vector<std::string>({"stdalign.h", "stdbool.h", "iso646.h"})) {
+ DeleteHeaders.insert(Key);
+ }
+}
+
+void IncludeModernizePPCallbacks::InclusionDirective(
+ SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+ bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
+ // FIXME: Take care of library symbols from the global namespace.
+ //
+ // Reasonable options for the check:
+ //
+ // 1. Insert std prefix for every such symbol occurrence.
+ // 2. Insert `using namespace std;` to the beginning of TU.
+ // 3. Do nothing and let the user deal with the migration himself.
+ if (CStyledHeaderToCxx.count(FileName) != 0) {
+ std::string Replacement =
+ (llvm::Twine("<") + CStyledHeaderToCxx[FileName] + ">").str();
+ Check.diag(FilenameRange.getBegin(), "inclusion of deprecated C++ header "
+ "'%0'; consider using '%1' instead")
+ << FileName << CStyledHeaderToCxx[FileName]
+ << FixItHint::CreateReplacement(FilenameRange.getAsRange(),
+ Replacement);
+ } else if (DeleteHeaders.count(FileName) != 0) {
+ Check.diag(FilenameRange.getBegin(),
+ "including '%0' has no effect in C++; consider removing it")
+ << FileName << FixItHint::CreateRemoval(
+ SourceRange(HashLoc, FilenameRange.getEnd()));
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DeprecatedHeadersCheck.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_MODERNIZE_C_HEADERS_TO_CXX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check replaces deprecated C library headers with their C++ STL
+/// alternatives.
+///
+/// Before:
+/// ~~~{.cpp}
+/// #include <header.h>
+/// ~~~
+///
+/// After:
+/// ~~~{.cpp}
+/// #include <cheader>
+/// ~~~
+///
+/// Example: ``<stdio.h> => <cstdio>``
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-headers.html
+class DeprecatedHeadersCheck : public ClangTidyCheck {
+public:
+ DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
--- /dev/null
+//===--- LoopConvertCheck.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 "LoopConvertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Casting.h"
+#include <cassert>
+#include <cstring>
+#include <utility>
+
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char LoopNameArray[] = "forLoopArray";
+static const char LoopNameIterator[] = "forLoopIterator";
+static const char LoopNamePseudoArray[] = "forLoopPseudoArray";
+static const char ConditionBoundName[] = "conditionBound";
+static const char ConditionVarName[] = "conditionVar";
+static const char IncrementVarName[] = "incrementVar";
+static const char InitVarName[] = "initVar";
+static const char BeginCallName[] = "beginCall";
+static const char EndCallName[] = "endCall";
+static const char ConditionEndVarName[] = "conditionEndVar";
+static const char EndVarName[] = "endVar";
+static const char DerefByValueResultName[] = "derefByValueResult";
+static const char DerefByRefResultName[] = "derefByRefResult";
+
+// shared matchers
+static const TypeMatcher AnyType = anything();
+
+static const StatementMatcher IntegerComparisonMatcher =
+ expr(ignoringParenImpCasts(
+ declRefExpr(to(varDecl(hasType(isInteger())).bind(ConditionVarName)))));
+
+static const DeclarationMatcher InitToZeroMatcher =
+ varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
+ .bind(InitVarName);
+
+static const StatementMatcher IncrementVarMatcher =
+ declRefExpr(to(varDecl(hasType(isInteger())).bind(IncrementVarName)));
+
+/// \brief The matcher for loops over arrays.
+///
+/// In this general example, assuming 'j' and 'k' are of integral type:
+/// \code
+/// for (int i = 0; j < 3 + 2; ++k) { ... }
+/// \endcode
+/// The following string identifiers are bound to these parts of the AST:
+/// ConditionVarName: 'j' (as a VarDecl)
+/// ConditionBoundName: '3 + 2' (as an Expr)
+/// InitVarName: 'i' (as a VarDecl)
+/// IncrementVarName: 'k' (as a VarDecl)
+/// LoopName: The entire for loop (as a ForStmt)
+///
+/// Client code will need to make sure that:
+/// - The three index variables identified by the matcher are the same
+/// VarDecl.
+/// - The index variable is only used as an array index.
+/// - All arrays indexed by the loop are the same.
+StatementMatcher makeArrayLoopMatcher() {
+ StatementMatcher ArrayBoundMatcher =
+ expr(hasType(isInteger())).bind(ConditionBoundName);
+
+ return forStmt(
+ unless(isInTemplateInstantiation()),
+ hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
+ hasCondition(anyOf(
+ binaryOperator(hasOperatorName("<"),
+ hasLHS(IntegerComparisonMatcher),
+ hasRHS(ArrayBoundMatcher)),
+ binaryOperator(hasOperatorName(">"), hasLHS(ArrayBoundMatcher),
+ hasRHS(IntegerComparisonMatcher)))),
+ hasIncrement(unaryOperator(hasOperatorName("++"),
+ hasUnaryOperand(IncrementVarMatcher))))
+ .bind(LoopNameArray);
+}
+
+/// \brief The matcher used for iterator-based for loops.
+///
+/// This matcher is more flexible than array-based loops. It will match
+/// catch loops of the following textual forms (regardless of whether the
+/// iterator type is actually a pointer type or a class type):
+///
+/// Assuming f, g, and h are of type containerType::iterator,
+/// \code
+/// for (containerType::iterator it = container.begin(),
+/// e = createIterator(); f != g; ++h) { ... }
+/// for (containerType::iterator it = container.begin();
+/// f != anotherContainer.end(); ++h) { ... }
+/// \endcode
+/// The following string identifiers are bound to the parts of the AST:
+/// InitVarName: 'it' (as a VarDecl)
+/// ConditionVarName: 'f' (as a VarDecl)
+/// LoopName: The entire for loop (as a ForStmt)
+/// In the first example only:
+/// EndVarName: 'e' (as a VarDecl)
+/// ConditionEndVarName: 'g' (as a VarDecl)
+/// In the second example only:
+/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
+///
+/// Client code will need to make sure that:
+/// - The iterator variables 'it', 'f', and 'h' are the same.
+/// - The two containers on which 'begin' and 'end' are called are the same.
+/// - If the end iterator variable 'g' is defined, it is the same as 'f'.
+StatementMatcher makeIteratorLoopMatcher() {
+ StatementMatcher BeginCallMatcher =
+ cxxMemberCallExpr(
+ argumentCountIs(0),
+ callee(cxxMethodDecl(anyOf(hasName("begin"), hasName("cbegin")))))
+ .bind(BeginCallName);
+
+ DeclarationMatcher InitDeclMatcher =
+ varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher),
+ materializeTemporaryExpr(
+ ignoringParenImpCasts(BeginCallMatcher)),
+ hasDescendant(BeginCallMatcher))))
+ .bind(InitVarName);
+
+ DeclarationMatcher EndDeclMatcher =
+ varDecl(hasInitializer(anything())).bind(EndVarName);
+
+ StatementMatcher EndCallMatcher = cxxMemberCallExpr(
+ argumentCountIs(0),
+ callee(cxxMethodDecl(anyOf(hasName("end"), hasName("cend")))));
+
+ StatementMatcher IteratorBoundMatcher =
+ expr(anyOf(ignoringParenImpCasts(
+ declRefExpr(to(varDecl().bind(ConditionEndVarName)))),
+ ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)),
+ materializeTemporaryExpr(ignoringParenImpCasts(
+ expr(EndCallMatcher).bind(EndCallName)))));
+
+ StatementMatcher IteratorComparisonMatcher = expr(
+ ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ConditionVarName)))));
+
+ auto OverloadedNEQMatcher = ignoringImplicit(
+ cxxOperatorCallExpr(hasOverloadedOperatorName("!="), argumentCountIs(2),
+ hasArgument(0, IteratorComparisonMatcher),
+ hasArgument(1, IteratorBoundMatcher)));
+
+ // This matcher tests that a declaration is a CXXRecordDecl that has an
+ // overloaded operator*(). If the operator*() returns by value instead of by
+ // reference then the return type is tagged with DerefByValueResultName.
+ internal::Matcher<VarDecl> TestDerefReturnsByValue =
+ hasType(cxxRecordDecl(hasMethod(allOf(
+ hasOverloadedOperatorName("*"),
+ anyOf(
+ // Tag the return type if it's by value.
+ returns(qualType(unless(hasCanonicalType(referenceType())))
+ .bind(DerefByValueResultName)),
+ returns(
+ // Skip loops where the iterator's operator* returns an
+ // rvalue reference. This is just weird.
+ qualType(unless(hasCanonicalType(rValueReferenceType())))
+ .bind(DerefByRefResultName)))))));
+
+ return forStmt(
+ unless(isInTemplateInstantiation()),
+ hasLoopInit(anyOf(declStmt(declCountIs(2),
+ containsDeclaration(0, InitDeclMatcher),
+ containsDeclaration(1, EndDeclMatcher)),
+ declStmt(hasSingleDecl(InitDeclMatcher)))),
+ hasCondition(
+ anyOf(binaryOperator(hasOperatorName("!="),
+ hasLHS(IteratorComparisonMatcher),
+ hasRHS(IteratorBoundMatcher)),
+ binaryOperator(hasOperatorName("!="),
+ hasLHS(IteratorBoundMatcher),
+ hasRHS(IteratorComparisonMatcher)),
+ OverloadedNEQMatcher)),
+ hasIncrement(anyOf(
+ unaryOperator(hasOperatorName("++"),
+ hasUnaryOperand(declRefExpr(
+ to(varDecl(hasType(pointsTo(AnyType)))
+ .bind(IncrementVarName))))),
+ cxxOperatorCallExpr(
+ hasOverloadedOperatorName("++"),
+ hasArgument(
+ 0, declRefExpr(to(varDecl(TestDerefReturnsByValue)
+ .bind(IncrementVarName))))))))
+ .bind(LoopNameIterator);
+}
+
+/// \brief The matcher used for array-like containers (pseudoarrays).
+///
+/// This matcher is more flexible than array-based loops. It will match
+/// loops of the following textual forms (regardless of whether the
+/// iterator type is actually a pointer type or a class type):
+///
+/// Assuming f, g, and h are of type containerType::iterator,
+/// \code
+/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
+/// for (int i = 0; f < container.size(); ++h) { ... }
+/// \endcode
+/// The following string identifiers are bound to the parts of the AST:
+/// InitVarName: 'i' (as a VarDecl)
+/// ConditionVarName: 'f' (as a VarDecl)
+/// LoopName: The entire for loop (as a ForStmt)
+/// In the first example only:
+/// EndVarName: 'j' (as a VarDecl)
+/// ConditionEndVarName: 'g' (as a VarDecl)
+/// In the second example only:
+/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
+///
+/// Client code will need to make sure that:
+/// - The index variables 'i', 'f', and 'h' are the same.
+/// - The containers on which 'size()' is called is the container indexed.
+/// - The index variable is only used in overloaded operator[] or
+/// container.at().
+/// - If the end iterator variable 'g' is defined, it is the same as 'j'.
+/// - The container's iterators would not be invalidated during the loop.
+StatementMatcher makePseudoArrayLoopMatcher() {
+ // Test that the incoming type has a record declaration that has methods
+ // called 'begin' and 'end'. If the incoming type is const, then make sure
+ // these methods are also marked const.
+ //
+ // FIXME: To be completely thorough this matcher should also ensure the
+ // return type of begin/end is an iterator that dereferences to the same as
+ // what operator[] or at() returns. Such a test isn't likely to fail except
+ // for pathological cases.
+ //
+ // FIXME: Also, a record doesn't necessarily need begin() and end(). Free
+ // functions called begin() and end() taking the container as an argument
+ // are also allowed.
+ TypeMatcher RecordWithBeginEnd = qualType(anyOf(
+ qualType(isConstQualified(),
+ hasDeclaration(cxxRecordDecl(
+ hasMethod(cxxMethodDecl(hasName("begin"), isConst())),
+ hasMethod(cxxMethodDecl(hasName("end"),
+ isConst())))) // hasDeclaration
+ ), // qualType
+ qualType(
+ unless(isConstQualified()),
+ hasDeclaration(cxxRecordDecl(hasMethod(hasName("begin")),
+ hasMethod(hasName("end"))))) // qualType
+ ));
+
+ StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
+ argumentCountIs(0),
+ callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))),
+ on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
+ hasType(RecordWithBeginEnd))));
+
+ StatementMatcher EndInitMatcher =
+ expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
+ explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
+ expr(SizeCallMatcher).bind(EndCallName))))));
+
+ DeclarationMatcher EndDeclMatcher =
+ varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
+
+ StatementMatcher IndexBoundMatcher =
+ expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
+ varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
+ EndInitMatcher));
+
+ return forStmt(
+ unless(isInTemplateInstantiation()),
+ hasLoopInit(
+ anyOf(declStmt(declCountIs(2),
+ containsDeclaration(0, InitToZeroMatcher),
+ containsDeclaration(1, EndDeclMatcher)),
+ declStmt(hasSingleDecl(InitToZeroMatcher)))),
+ hasCondition(anyOf(
+ binaryOperator(hasOperatorName("<"),
+ hasLHS(IntegerComparisonMatcher),
+ hasRHS(IndexBoundMatcher)),
+ binaryOperator(hasOperatorName(">"), hasLHS(IndexBoundMatcher),
+ hasRHS(IntegerComparisonMatcher)))),
+ hasIncrement(unaryOperator(hasOperatorName("++"),
+ hasUnaryOperand(IncrementVarMatcher))))
+ .bind(LoopNamePseudoArray);
+}
+
+/// \brief Determine whether Init appears to be an initializing an iterator.
+///
+/// If it is, returns the object whose begin() or end() method is called, and
+/// the output parameter isArrow is set to indicate whether the initialization
+/// is called via . or ->.
+static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
+ bool *IsArrow) {
+ // FIXME: Maybe allow declaration/initialization outside of the for loop.
+ const auto *TheCall =
+ dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init));
+ if (!TheCall || TheCall->getNumArgs() != 0)
+ return nullptr;
+
+ const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
+ if (!Member)
+ return nullptr;
+ StringRef Name = Member->getMemberDecl()->getName();
+ StringRef TargetName = IsBegin ? "begin" : "end";
+ StringRef ConstTargetName = IsBegin ? "cbegin" : "cend";
+ if (Name != TargetName && Name != ConstTargetName)
+ return nullptr;
+
+ const Expr *SourceExpr = Member->getBase();
+ if (!SourceExpr)
+ return nullptr;
+
+ *IsArrow = Member->isArrow();
+ return SourceExpr;
+}
+
+/// \brief Determines the container whose begin() and end() functions are called
+/// for an iterator-based loop.
+///
+/// BeginExpr must be a member call to a function named "begin()", and EndExpr
+/// must be a member.
+static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr,
+ const Expr *EndExpr,
+ bool *ContainerNeedsDereference) {
+ // Now that we know the loop variable and test expression, make sure they are
+ // valid.
+ bool BeginIsArrow = false;
+ bool EndIsArrow = false;
+ const Expr *BeginContainerExpr =
+ getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow);
+ if (!BeginContainerExpr)
+ return nullptr;
+
+ const Expr *EndContainerExpr =
+ getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow);
+ // Disallow loops that try evil things like this (note the dot and arrow):
+ // for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
+ if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
+ !areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
+ return nullptr;
+
+ *ContainerNeedsDereference = BeginIsArrow;
+ return BeginContainerExpr;
+}
+
+/// \brief Obtain the original source code text from a SourceRange.
+static StringRef getStringFromRange(SourceManager &SourceMgr,
+ const LangOptions &LangOpts,
+ SourceRange Range) {
+ if (SourceMgr.getFileID(Range.getBegin()) !=
+ SourceMgr.getFileID(Range.getEnd())) {
+ return StringRef(); // Empty string.
+ }
+
+ return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
+ LangOpts);
+}
+
+/// \brief If the given expression is actually a DeclRefExpr or a MemberExpr,
+/// find and return the underlying ValueDecl; otherwise, return NULL.
+static const ValueDecl *getReferencedVariable(const Expr *E) {
+ if (const DeclRefExpr *DRE = getDeclRef(E))
+ return dyn_cast<VarDecl>(DRE->getDecl());
+ if (const auto *Mem = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
+ return dyn_cast<FieldDecl>(Mem->getMemberDecl());
+ return nullptr;
+}
+
+/// \brief Returns true when the given expression is a member expression
+/// whose base is `this` (implicitly or not).
+static bool isDirectMemberExpr(const Expr *E) {
+ if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
+ return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
+ return false;
+}
+
+/// \brief Given an expression that represents an usage of an element from the
+/// containter that we are iterating over, returns false when it can be
+/// guaranteed this element cannot be modified as a result of this usage.
+static bool canBeModified(ASTContext *Context, const Expr *E) {
+ if (E->getType().isConstQualified())
+ return false;
+ auto Parents = Context->getParents(*E);
+ if (Parents.size() != 1)
+ return true;
+ if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) {
+ if ((Cast->getCastKind() == CK_NoOp &&
+ Cast->getType() == E->getType().withConst()) ||
+ (Cast->getCastKind() == CK_LValueToRValue &&
+ !Cast->getType().isNull() && Cast->getType()->isFundamentalType()))
+ return false;
+ }
+ // FIXME: Make this function more generic.
+ return true;
+}
+
+/// \brief Returns true when it can be guaranteed that the elements of the
+/// container are not being modified.
+static bool usagesAreConst(ASTContext *Context, const UsageResult &Usages) {
+ for (const Usage &U : Usages) {
+ // Lambda captures are just redeclarations (VarDecl) of the same variable,
+ // not expressions. If we want to know if a variable that is captured by
+ // reference can be modified in an usage inside the lambda's body, we need
+ // to find the expression corresponding to that particular usage, later in
+ // this loop.
+ if (U.Kind != Usage::UK_CaptureByCopy && U.Kind != Usage::UK_CaptureByRef &&
+ canBeModified(Context, U.Expression))
+ return false;
+ }
+ return true;
+}
+
+/// \brief Returns true if the elements of the container are never accessed
+/// by reference.
+static bool usagesReturnRValues(const UsageResult &Usages) {
+ for (const auto &U : Usages) {
+ if (U.Expression && !U.Expression->isRValue())
+ return false;
+ }
+ return true;
+}
+
+/// \brief Returns true if the container is const-qualified.
+static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) {
+ if (const auto *VDec = getReferencedVariable(ContainerExpr)) {
+ QualType CType = VDec->getType();
+ if (Dereference) {
+ if (!CType->isPointerType())
+ return false;
+ CType = CType->getPointeeType();
+ }
+ // If VDec is a reference to a container, Dereference is false,
+ // but we still need to check the const-ness of the underlying container
+ // type.
+ CType = CType.getNonReferenceType();
+ return CType.isConstQualified();
+ }
+ return false;
+}
+
+LoopConvertCheck::RangeDescriptor::RangeDescriptor()
+ : ContainerNeedsDereference(false), DerefByConstRef(false),
+ DerefByValue(false) {}
+
+LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
+ MaxCopySize(std::stoull(Options.get("MaxCopySize", "16"))),
+ MinConfidence(StringSwitch<Confidence::Level>(
+ Options.get("MinConfidence", "reasonable"))
+ .Case("safe", Confidence::CL_Safe)
+ .Case("risky", Confidence::CL_Risky)
+ .Default(Confidence::CL_Reasonable)),
+ NamingStyle(StringSwitch<VariableNamer::NamingStyle>(
+ Options.get("NamingStyle", "CamelCase"))
+ .Case("camelBack", VariableNamer::NS_CamelBack)
+ .Case("lower_case", VariableNamer::NS_LowerCase)
+ .Case("UPPER_CASE", VariableNamer::NS_UpperCase)
+ .Default(VariableNamer::NS_CamelCase)) {}
+
+void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "MaxCopySize", std::to_string(MaxCopySize));
+ SmallVector<std::string, 3> Confs{"risky", "reasonable", "safe"};
+ Options.store(Opts, "MinConfidence", Confs[static_cast<int>(MinConfidence)]);
+
+ SmallVector<std::string, 4> Styles{"camelBack", "CamelCase", "lower_case",
+ "UPPER_CASE"};
+ Options.store(Opts, "NamingStyle", Styles[static_cast<int>(NamingStyle)]);
+}
+
+void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++. Because this checker is used for
+ // modernization, it is reasonable to run it on any C++ standard with the
+ // assumption the user is trying to modernize their codebase.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(makeArrayLoopMatcher(), this);
+ Finder->addMatcher(makeIteratorLoopMatcher(), this);
+ Finder->addMatcher(makePseudoArrayLoopMatcher(), this);
+}
+
+/// \brief Given the range of a single declaration, such as:
+/// \code
+/// unsigned &ThisIsADeclarationThatCanSpanSeveralLinesOfCode =
+/// InitializationValues[I];
+/// next_instruction;
+/// \endcode
+/// Finds the range that has to be erased to remove this declaration without
+/// leaving empty lines, by extending the range until the beginning of the
+/// next instruction.
+///
+/// We need to delete a potential newline after the deleted alias, as
+/// clang-format will leave empty lines untouched. For all other formatting we
+/// rely on clang-format to fix it.
+void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &Range) {
+ bool Invalid = false;
+ const char *TextAfter =
+ SM.getCharacterData(Range.getEnd().getLocWithOffset(1), &Invalid);
+ if (Invalid)
+ return;
+ unsigned Offset = std::strspn(TextAfter, " \t\r\n");
+ Range =
+ SourceRange(Range.getBegin(), Range.getEnd().getLocWithOffset(Offset));
+}
+
+/// \brief Computes the changes needed to convert a given for loop, and
+/// applies them.
+void LoopConvertCheck::doConversion(
+ ASTContext *Context, const VarDecl *IndexVar,
+ const ValueDecl *MaybeContainer, const UsageResult &Usages,
+ const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit,
+ const ForStmt *Loop, RangeDescriptor Descriptor) {
+ auto Diag = diag(Loop->getForLoc(), "use range-based for loop instead");
+
+ std::string VarName;
+ bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl;
+ bool AliasVarIsRef = false;
+ bool CanCopy = true;
+
+ if (VarNameFromAlias) {
+ const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
+ VarName = AliasVar->getName().str();
+
+ // Use the type of the alias if it's not the same
+ QualType AliasVarType = AliasVar->getType();
+ assert(!AliasVarType.isNull() && "Type in VarDecl is null");
+ if (AliasVarType->isReferenceType()) {
+ AliasVarType = AliasVarType.getNonReferenceType();
+ AliasVarIsRef = true;
+ }
+ if (Descriptor.ElemType.isNull() ||
+ !Context->hasSameUnqualifiedType(AliasVarType, Descriptor.ElemType))
+ Descriptor.ElemType = AliasVarType;
+
+ // We keep along the entire DeclStmt to keep the correct range here.
+ SourceRange ReplaceRange = AliasDecl->getSourceRange();
+
+ std::string ReplacementText;
+ if (AliasUseRequired) {
+ ReplacementText = VarName;
+ } else if (AliasFromForInit) {
+ // FIXME: Clang includes the location of the ';' but only for DeclStmt's
+ // in a for loop's init clause. Need to put this ';' back while removing
+ // the declaration of the alias variable. This is probably a bug.
+ ReplacementText = ";";
+ } else {
+ // Avoid leaving empty lines or trailing whitespaces.
+ getAliasRange(Context->getSourceManager(), ReplaceRange);
+ }
+
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ReplaceRange), ReplacementText);
+ // No further replacements are made to the loop, since the iterator or index
+ // was used exactly once - in the initialization of AliasVar.
+ } else {
+ VariableNamer Namer(&TUInfo->getGeneratedDecls(),
+ &TUInfo->getParentFinder().getStmtToParentStmtMap(),
+ Loop, IndexVar, MaybeContainer, Context, NamingStyle);
+ VarName = Namer.createIndexName();
+ // First, replace all usages of the array subscript expression with our new
+ // variable.
+ for (const auto &Usage : Usages) {
+ std::string ReplaceText;
+ SourceRange Range = Usage.Range;
+ if (Usage.Expression) {
+ // If this is an access to a member through the arrow operator, after
+ // the replacement it must be accessed through the '.' operator.
+ ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow ? VarName + "."
+ : VarName;
+ auto Parents = Context->getParents(*Usage.Expression);
+ if (Parents.size() == 1) {
+ if (const auto *Paren = Parents[0].get<ParenExpr>()) {
+ // Usage.Expression will be replaced with the new index variable,
+ // and parenthesis around a simple DeclRefExpr can always be
+ // removed.
+ Range = Paren->getSourceRange();
+ } else if (const auto *UOP = Parents[0].get<UnaryOperator>()) {
+ // If we are taking the address of the loop variable, then we must
+ // not use a copy, as it would mean taking the address of the loop's
+ // local index instead.
+ // FIXME: This won't catch cases where the address is taken outside
+ // of the loop's body (for instance, in a function that got the
+ // loop's index as a const reference parameter), or where we take
+ // the address of a member (like "&Arr[i].A.B.C").
+ if (UOP->getOpcode() == UO_AddrOf)
+ CanCopy = false;
+ }
+ }
+ } else {
+ // The Usage expression is only null in case of lambda captures (which
+ // are VarDecl). If the index is captured by value, add '&' to capture
+ // by reference instead.
+ ReplaceText =
+ Usage.Kind == Usage::UK_CaptureByCopy ? "&" + VarName : VarName;
+ }
+ TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar));
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Range), ReplaceText);
+ }
+ }
+
+ // Now, we need to construct the new range expression.
+ SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc());
+
+ QualType Type = Context->getAutoDeductType();
+ if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType())
+ Type = Descriptor.ElemType.getUnqualifiedType();
+
+ // If the new variable name is from the aliased variable, then the reference
+ // type for the new variable should only be used if the aliased variable was
+ // declared as a reference.
+ bool IsCheapToCopy =
+ !Descriptor.ElemType.isNull() &&
+ Descriptor.ElemType.isTriviallyCopyableType(*Context) &&
+ // TypeInfo::Width is in bits.
+ Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize;
+ bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) ||
+ (Descriptor.DerefByConstRef && IsCheapToCopy));
+
+ if (!UseCopy) {
+ if (Descriptor.DerefByConstRef) {
+ Type = Context->getLValueReferenceType(Context->getConstType(Type));
+ } else if (Descriptor.DerefByValue) {
+ if (!IsCheapToCopy)
+ Type = Context->getRValueReferenceType(Type);
+ } else {
+ Type = Context->getLValueReferenceType(Type);
+ }
+ }
+
+ StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? "*" : "";
+ std::string TypeString = Type.getAsString(getLangOpts());
+ std::string Range = ("(" + TypeString + " " + VarName + " : " +
+ MaybeDereference + Descriptor.ContainerString + ")")
+ .str();
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ParenRange), Range);
+ TUInfo->getGeneratedDecls().insert(make_pair(Loop, VarName));
+}
+
+/// \brief Returns a string which refers to the container iterated over.
+StringRef LoopConvertCheck::getContainerString(ASTContext *Context,
+ const ForStmt *Loop,
+ const Expr *ContainerExpr) {
+ StringRef ContainerString;
+ if (isa<CXXThisExpr>(ContainerExpr->IgnoreParenImpCasts())) {
+ ContainerString = "this";
+ } else {
+ ContainerString =
+ getStringFromRange(Context->getSourceManager(), Context->getLangOpts(),
+ ContainerExpr->getSourceRange());
+ }
+
+ return ContainerString;
+}
+
+/// \brief Determines what kind of 'auto' must be used after converting a for
+/// loop that iterates over an array or pseudoarray.
+void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context,
+ const BoundNodes &Nodes,
+ const Expr *ContainerExpr,
+ const UsageResult &Usages,
+ RangeDescriptor &Descriptor) {
+ // On arrays and pseudoarrays, we must figure out the qualifiers from the
+ // usages.
+ if (usagesAreConst(Context, Usages) ||
+ containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) {
+ Descriptor.DerefByConstRef = true;
+ }
+ if (usagesReturnRValues(Usages)) {
+ // If the index usages (dereference, subscript, at, ...) return rvalues,
+ // then we should not use a reference, because we need to keep the code
+ // correct if it mutates the returned objects.
+ Descriptor.DerefByValue = true;
+ }
+ // Try to find the type of the elements on the container, to check if
+ // they are trivially copyable.
+ for (const Usage &U : Usages) {
+ if (!U.Expression || U.Expression->getType().isNull())
+ continue;
+ QualType Type = U.Expression->getType().getCanonicalType();
+ if (U.Kind == Usage::UK_MemberThroughArrow) {
+ if (!Type->isPointerType()) {
+ continue;
+ }
+ Type = Type->getPointeeType();
+ }
+ Descriptor.ElemType = Type;
+ }
+}
+
+/// \brief Determines what kind of 'auto' must be used after converting an
+/// iterator based for loop.
+void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context,
+ const BoundNodes &Nodes,
+ RangeDescriptor &Descriptor) {
+ // The matchers for iterator loops provide bound nodes to obtain this
+ // information.
+ const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName);
+ QualType CanonicalInitVarType = InitVar->getType().getCanonicalType();
+ const auto *DerefByValueType =
+ Nodes.getNodeAs<QualType>(DerefByValueResultName);
+ Descriptor.DerefByValue = DerefByValueType;
+
+ if (Descriptor.DerefByValue) {
+ // If the dereference operator returns by value then test for the
+ // canonical const qualification of the init variable type.
+ Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified();
+ Descriptor.ElemType = *DerefByValueType;
+ } else {
+ if (const auto *DerefType =
+ Nodes.getNodeAs<QualType>(DerefByRefResultName)) {
+ // A node will only be bound with DerefByRefResultName if we're dealing
+ // with a user-defined iterator type. Test the const qualification of
+ // the reference type.
+ auto ValueType = DerefType->getNonReferenceType();
+
+ Descriptor.DerefByConstRef = ValueType.isConstQualified();
+ Descriptor.ElemType = ValueType;
+ } else {
+ // By nature of the matcher this case is triggered only for built-in
+ // iterator types (i.e. pointers).
+ assert(isa<PointerType>(CanonicalInitVarType) &&
+ "Non-class iterator type is not a pointer type");
+
+ // We test for const qualification of the pointed-at type.
+ Descriptor.DerefByConstRef =
+ CanonicalInitVarType->getPointeeType().isConstQualified();
+ Descriptor.ElemType = CanonicalInitVarType->getPointeeType();
+ }
+ }
+}
+
+/// \brief Determines the parameters needed to build the range replacement.
+void LoopConvertCheck::determineRangeDescriptor(
+ ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop,
+ LoopFixerKind FixerKind, const Expr *ContainerExpr,
+ const UsageResult &Usages, RangeDescriptor &Descriptor) {
+ Descriptor.ContainerString = getContainerString(Context, Loop, ContainerExpr);
+
+ if (FixerKind == LFK_Iterator)
+ getIteratorLoopQualifiers(Context, Nodes, Descriptor);
+ else
+ getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor);
+}
+
+/// \brief Check some of the conditions that must be met for the loop to be
+/// convertible.
+bool LoopConvertCheck::isConvertible(ASTContext *Context,
+ const ast_matchers::BoundNodes &Nodes,
+ const ForStmt *Loop,
+ LoopFixerKind FixerKind) {
+ // If we already modified the range of this for loop, don't do any further
+ // updates on this iteration.
+ if (TUInfo->getReplacedVars().count(Loop))
+ return false;
+
+ // Check that we have exactly one index variable and at most one end variable.
+ const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName);
+ const auto *CondVar = Nodes.getNodeAs<VarDecl>(ConditionVarName);
+ const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName);
+ if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
+ return false;
+ const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName);
+ const auto *ConditionEndVar = Nodes.getNodeAs<VarDecl>(ConditionEndVarName);
+ if (EndVar && !areSameVariable(EndVar, ConditionEndVar))
+ return false;
+
+ // FIXME: Try to put most of this logic inside a matcher.
+ if (FixerKind == LFK_Iterator) {
+ QualType InitVarType = InitVar->getType();
+ QualType CanonicalInitVarType = InitVarType.getCanonicalType();
+
+ const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName);
+ assert(BeginCall && "Bad Callback. No begin call expression");
+ QualType CanonicalBeginType =
+ BeginCall->getMethodDecl()->getReturnType().getCanonicalType();
+ if (CanonicalBeginType->isPointerType() &&
+ CanonicalInitVarType->isPointerType()) {
+ // If the initializer and the variable are both pointers check if the
+ // un-qualified pointee types match, otherwise we don't use auto.
+ if (!Context->hasSameUnqualifiedType(
+ CanonicalBeginType->getPointeeType(),
+ CanonicalInitVarType->getPointeeType()))
+ return false;
+ } else if (!Context->hasSameType(CanonicalInitVarType,
+ CanonicalBeginType)) {
+ // Check for qualified types to avoid conversions from non-const to const
+ // iterator types.
+ return false;
+ }
+ } else if (FixerKind == LFK_PseudoArray) {
+ // This call is required to obtain the container.
+ const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName);
+ if (!EndCall || !dyn_cast<MemberExpr>(EndCall->getCallee()))
+ return false;
+ }
+ return true;
+}
+
+void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
+ const BoundNodes &Nodes = Result.Nodes;
+ Confidence ConfidenceLevel(Confidence::CL_Safe);
+ ASTContext *Context = Result.Context;
+
+ const ForStmt *Loop;
+ LoopFixerKind FixerKind;
+ RangeDescriptor Descriptor;
+
+ if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameArray))) {
+ FixerKind = LFK_Array;
+ } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameIterator))) {
+ FixerKind = LFK_Iterator;
+ } else {
+ Loop = Nodes.getNodeAs<ForStmt>(LoopNamePseudoArray);
+ assert(Loop && "Bad Callback. No for statement");
+ FixerKind = LFK_PseudoArray;
+ }
+
+ if (!isConvertible(Context, Nodes, Loop, FixerKind))
+ return;
+
+ const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName);
+ const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName);
+
+ // If the loop calls end()/size() after each iteration, lower our confidence
+ // level.
+ if (FixerKind != LFK_Array && !EndVar)
+ ConfidenceLevel.lowerTo(Confidence::CL_Reasonable);
+
+ // If the end comparison isn't a variable, we can try to work with the
+ // expression the loop variable is being tested against instead.
+ const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName);
+ const auto *BoundExpr = Nodes.getNodeAs<Expr>(ConditionBoundName);
+
+ // Find container expression of iterators and pseudoarrays, and determine if
+ // this expression needs to be dereferenced to obtain the container.
+ // With array loops, the container is often discovered during the
+ // ForLoopIndexUseVisitor traversal.
+ const Expr *ContainerExpr = nullptr;
+ if (FixerKind == LFK_Iterator) {
+ ContainerExpr = findContainer(Context, LoopVar->getInit(),
+ EndVar ? EndVar->getInit() : EndCall,
+ &Descriptor.ContainerNeedsDereference);
+ } else if (FixerKind == LFK_PseudoArray) {
+ ContainerExpr = EndCall->getImplicitObjectArgument();
+ Descriptor.ContainerNeedsDereference =
+ dyn_cast<MemberExpr>(EndCall->getCallee())->isArrow();
+ }
+
+ // We must know the container or an array length bound.
+ if (!ContainerExpr && !BoundExpr)
+ return;
+
+ ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
+ BoundExpr,
+ Descriptor.ContainerNeedsDereference);
+
+ // Find expressions and variables on which the container depends.
+ if (ContainerExpr) {
+ ComponentFinderASTVisitor ComponentFinder;
+ ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts());
+ Finder.addComponents(ComponentFinder.getComponents());
+ }
+
+ // Find usages of the loop index. If they are not used in a convertible way,
+ // stop here.
+ if (!Finder.findAndVerifyUsages(Loop->getBody()))
+ return;
+ ConfidenceLevel.lowerTo(Finder.getConfidenceLevel());
+
+ // Obtain the container expression, if we don't have it yet.
+ if (FixerKind == LFK_Array) {
+ ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
+
+ // Very few loops are over expressions that generate arrays rather than
+ // array variables. Consider loops over arrays that aren't just represented
+ // by a variable to be risky conversions.
+ if (!getReferencedVariable(ContainerExpr) &&
+ !isDirectMemberExpr(ContainerExpr))
+ ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+ }
+
+ // Find out which qualifiers we have to use in the loop range.
+ const UsageResult &Usages = Finder.getUsages();
+ determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr,
+ Usages, Descriptor);
+
+ // Ensure that we do not try to move an expression dependent on a local
+ // variable declared inside the loop outside of it.
+ // FIXME: Determine when the external dependency isn't an expression converted
+ // by another loop.
+ TUInfo->getParentFinder().gatherAncestors(Context->getTranslationUnitDecl());
+ DependencyFinderASTVisitor DependencyFinder(
+ &TUInfo->getParentFinder().getStmtToParentStmtMap(),
+ &TUInfo->getParentFinder().getDeclToParentStmtMap(),
+ &TUInfo->getReplacedVars(), Loop);
+
+ if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) ||
+ Descriptor.ContainerString.empty() || Usages.empty() ||
+ ConfidenceLevel.getLevel() < MinConfidence)
+ return;
+
+ doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages,
+ Finder.getAliasDecl(), Finder.aliasUseRequired(),
+ Finder.aliasFromForInit(), Loop, Descriptor);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- LoopConvertCheck.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_MODERNIZE_LOOP_CONVERT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
+
+#include "../ClangTidy.h"
+#include "LoopConvertUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class LoopConvertCheck : public ClangTidyCheck {
+public:
+ LoopConvertCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ struct RangeDescriptor {
+ RangeDescriptor();
+ bool ContainerNeedsDereference;
+ bool DerefByConstRef;
+ bool DerefByValue;
+ std::string ContainerString;
+ QualType ElemType;
+ };
+
+ void getAliasRange(SourceManager &SM, SourceRange &DeclRange);
+
+ void doConversion(ASTContext *Context, const VarDecl *IndexVar,
+ const ValueDecl *MaybeContainer, const UsageResult &Usages,
+ const DeclStmt *AliasDecl, bool AliasUseRequired,
+ bool AliasFromForInit, const ForStmt *Loop,
+ RangeDescriptor Descriptor);
+
+ StringRef getContainerString(ASTContext *Context, const ForStmt *Loop,
+ const Expr *ContainerExpr);
+
+ void getArrayLoopQualifiers(ASTContext *Context,
+ const ast_matchers::BoundNodes &Nodes,
+ const Expr *ContainerExpr,
+ const UsageResult &Usages,
+ RangeDescriptor &Descriptor);
+
+ void getIteratorLoopQualifiers(ASTContext *Context,
+ const ast_matchers::BoundNodes &Nodes,
+ RangeDescriptor &Descriptor);
+
+ void determineRangeDescriptor(ASTContext *Context,
+ const ast_matchers::BoundNodes &Nodes,
+ const ForStmt *Loop, LoopFixerKind FixerKind,
+ const Expr *ContainerExpr,
+ const UsageResult &Usages,
+ RangeDescriptor &Descriptor);
+
+ bool isConvertible(ASTContext *Context, const ast_matchers::BoundNodes &Nodes,
+ const ForStmt *Loop, LoopFixerKind FixerKind);
+
+ std::unique_ptr<TUTrackingInfo> TUInfo;
+ const unsigned long long MaxCopySize;
+ const Confidence::Level MinConfidence;
+ const VariableNamer::NamingStyle NamingStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
--- /dev/null
+//===--- LoopConvertUtils.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 "LoopConvertUtils.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/Lambda.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Tracks a stack of parent statements during traversal.
+///
+/// All this really does is inject push_back() before running
+/// RecursiveASTVisitor::TraverseStmt() and pop_back() afterwards. The Stmt atop
+/// the stack is the parent of the current statement (NULL for the topmost
+/// statement).
+bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
+ StmtAncestors.insert(std::make_pair(Statement, StmtStack.back()));
+ StmtStack.push_back(Statement);
+ RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
+ StmtStack.pop_back();
+ return true;
+}
+
+/// \brief Keep track of the DeclStmt associated with each VarDecl.
+///
+/// Combined with StmtAncestors, this provides roughly the same information as
+/// Scope, as we can map a VarDecl to its DeclStmt, then walk up the parent tree
+/// using StmtAncestors.
+bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Decls) {
+ for (const auto *decl : Decls->decls()) {
+ if (const auto *V = dyn_cast<VarDecl>(decl))
+ DeclParents.insert(std::make_pair(V, Decls));
+ }
+ return true;
+}
+
+/// \brief record the DeclRefExpr as part of the parent expression.
+bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
+ Components.push_back(E);
+ return true;
+}
+
+/// \brief record the MemberExpr as part of the parent expression.
+bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
+ Components.push_back(Member);
+ return true;
+}
+
+/// \brief Forward any DeclRefExprs to a check on the referenced variable
+/// declaration.
+bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+ if (auto *V = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+ return VisitVarDecl(V);
+ return true;
+}
+
+/// \brief Determine if any this variable is declared inside the ContainingStmt.
+bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *V) {
+ const Stmt *Curr = DeclParents->lookup(V);
+ // First, see if the variable was declared within an inner scope of the loop.
+ while (Curr != nullptr) {
+ if (Curr == ContainingStmt) {
+ DependsOnInsideVariable = true;
+ return false;
+ }
+ Curr = StmtParents->lookup(Curr);
+ }
+
+ // Next, check if the variable was removed from existence by an earlier
+ // iteration.
+ for (const auto &I : *ReplacedVars) {
+ if (I.second == V) {
+ DependsOnInsideVariable = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+/// \brief If we already created a variable for TheLoop, check to make sure
+/// that the name was not already taken.
+bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
+ StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(TheLoop);
+ if (I != GeneratedDecls->end() && I->second == Name) {
+ Found = true;
+ return false;
+ }
+ return true;
+}
+
+/// \brief If any named declaration within the AST subtree has the same name,
+/// then consider Name already taken.
+bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *D) {
+ const IdentifierInfo *Ident = D->getIdentifier();
+ if (Ident && Ident->getName() == Name) {
+ Found = true;
+ return false;
+ }
+ return true;
+}
+
+/// \brief Forward any declaration references to the actual check on the
+/// referenced declaration.
+bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+ if (auto *D = dyn_cast<NamedDecl>(DeclRef->getDecl()))
+ return VisitNamedDecl(D);
+ return true;
+}
+
+/// \brief If the new variable name conflicts with any type used in the loop,
+/// then we mark that variable name as taken.
+bool DeclFinderASTVisitor::VisitTypeLoc(TypeLoc TL) {
+ QualType QType = TL.getType();
+
+ // Check if our name conflicts with a type, to handle for typedefs.
+ if (QType.getAsString() == Name) {
+ Found = true;
+ return false;
+ }
+ // Check for base type conflicts. For example, when a struct is being
+ // referenced in the body of the loop, the above getAsString() will return the
+ // whole type (ex. "struct s"), but will be caught here.
+ if (const IdentifierInfo *Ident = QType.getBaseTypeIdentifier()) {
+ if (Ident->getName() == Name) {
+ Found = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+/// \brief Look through conversion/copy constructors to find the explicit
+/// initialization expression, returning it is found.
+///
+/// The main idea is that given
+/// vector<int> v;
+/// we consider either of these initializations
+/// vector<int>::iterator it = v.begin();
+/// vector<int>::iterator it(v.begin());
+/// and retrieve `v.begin()` as the expression used to initialize `it` but do
+/// not include
+/// vector<int>::iterator it;
+/// vector<int>::iterator it(v.begin(), 0); // if this constructor existed
+/// as being initialized from `v.begin()`
+const Expr *digThroughConstructors(const Expr *E) {
+ if (!E)
+ return nullptr;
+ E = E->IgnoreImplicit();
+ if (const auto *ConstructExpr = dyn_cast<CXXConstructExpr>(E)) {
+ // The initial constructor must take exactly one parameter, but base class
+ // and deferred constructors can take more.
+ if (ConstructExpr->getNumArgs() != 1 ||
+ ConstructExpr->getConstructionKind() != CXXConstructExpr::CK_Complete)
+ return nullptr;
+ E = ConstructExpr->getArg(0);
+ if (const auto *Temp = dyn_cast<MaterializeTemporaryExpr>(E))
+ E = Temp->GetTemporaryExpr();
+ return digThroughConstructors(E);
+ }
+ return E;
+}
+
+/// \brief Returns true when two Exprs are equivalent.
+bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second) {
+ if (!First || !Second)
+ return false;
+
+ llvm::FoldingSetNodeID FirstID, SecondID;
+ First->Profile(FirstID, *Context, true);
+ Second->Profile(SecondID, *Context, true);
+ return FirstID == SecondID;
+}
+
+/// \brief Returns the DeclRefExpr represented by E, or NULL if there isn't one.
+const DeclRefExpr *getDeclRef(const Expr *E) {
+ return dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
+}
+
+/// \brief Returns true when two ValueDecls are the same variable.
+bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
+ return First && Second &&
+ First->getCanonicalDecl() == Second->getCanonicalDecl();
+}
+
+/// \brief Determines if an expression is a declaration reference to a
+/// particular variable.
+static bool exprReferencesVariable(const ValueDecl *Target, const Expr *E) {
+ if (!Target || !E)
+ return false;
+ const DeclRefExpr *Decl = getDeclRef(E);
+ return Decl && areSameVariable(Target, Decl->getDecl());
+}
+
+/// \brief If the expression is a dereference or call to operator*(), return the
+/// operand. Otherwise, return NULL.
+static const Expr *getDereferenceOperand(const Expr *E) {
+ if (const auto *Uop = dyn_cast<UnaryOperator>(E))
+ return Uop->getOpcode() == UO_Deref ? Uop->getSubExpr() : nullptr;
+
+ if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+ return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1
+ ? OpCall->getArg(0)
+ : nullptr;
+ }
+
+ return nullptr;
+}
+
+/// \brief Returns true when the Container contains an Expr equivalent to E.
+template <typename ContainerT>
+static bool containsExpr(ASTContext *Context, const ContainerT *Container,
+ const Expr *E) {
+ llvm::FoldingSetNodeID ID;
+ E->Profile(ID, *Context, true);
+ for (const auto &I : *Container) {
+ if (ID == I.second)
+ return true;
+ }
+ return false;
+}
+
+/// \brief Returns true when the index expression is a declaration reference to
+/// IndexVar.
+///
+/// If the index variable is `index`, this function returns true on
+/// arrayExpression[index];
+/// containerExpression[index];
+/// but not
+/// containerExpression[notIndex];
+static bool isIndexInSubscriptExpr(const Expr *IndexExpr,
+ const VarDecl *IndexVar) {
+ const DeclRefExpr *Idx = getDeclRef(IndexExpr);
+ return Idx && Idx->getType()->isIntegerType() &&
+ areSameVariable(IndexVar, Idx->getDecl());
+}
+
+/// \brief Returns true when the index expression is a declaration reference to
+/// IndexVar, Obj is the same expression as SourceExpr after all parens and
+/// implicit casts are stripped off.
+///
+/// If PermitDeref is true, IndexExpression may
+/// be a dereference (overloaded or builtin operator*).
+///
+/// This function is intended for array-like containers, as it makes sure that
+/// both the container and the index match.
+/// If the loop has index variable `index` and iterates over `container`, then
+/// isIndexInSubscriptExpr returns true for
+/// \code
+/// container[index]
+/// container.at(index)
+/// container->at(index)
+/// \endcode
+/// but not for
+/// \code
+/// container[notIndex]
+/// notContainer[index]
+/// \endcode
+/// If PermitDeref is true, then isIndexInSubscriptExpr additionally returns
+/// true on these expressions:
+/// \code
+/// (*container)[index]
+/// (*container).at(index)
+/// \endcode
+static bool isIndexInSubscriptExpr(ASTContext *Context, const Expr *IndexExpr,
+ const VarDecl *IndexVar, const Expr *Obj,
+ const Expr *SourceExpr, bool PermitDeref) {
+ if (!SourceExpr || !Obj || !isIndexInSubscriptExpr(IndexExpr, IndexVar))
+ return false;
+
+ if (areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
+ Obj->IgnoreParenImpCasts()))
+ return true;
+
+ if (const Expr *InnerObj = getDereferenceOperand(Obj->IgnoreParenImpCasts()))
+ if (PermitDeref && areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
+ InnerObj->IgnoreParenImpCasts()))
+ return true;
+
+ return false;
+}
+
+/// \brief Returns true when Opcall is a call a one-parameter dereference of
+/// IndexVar.
+///
+/// For example, if the index variable is `index`, returns true for
+/// *index
+/// but not
+/// index
+/// *notIndex
+static bool isDereferenceOfOpCall(const CXXOperatorCallExpr *OpCall,
+ const VarDecl *IndexVar) {
+ return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1 &&
+ exprReferencesVariable(IndexVar, OpCall->getArg(0));
+}
+
+/// \brief Returns true when Uop is a dereference of IndexVar.
+///
+/// For example, if the index variable is `index`, returns true for
+/// *index
+/// but not
+/// index
+/// *notIndex
+static bool isDereferenceOfUop(const UnaryOperator *Uop,
+ const VarDecl *IndexVar) {
+ return Uop->getOpcode() == UO_Deref &&
+ exprReferencesVariable(IndexVar, Uop->getSubExpr());
+}
+
+/// \brief Determines whether the given Decl defines a variable initialized to
+/// the loop object.
+///
+/// This is intended to find cases such as
+/// \code
+/// for (int i = 0; i < arraySize(arr); ++i) {
+/// T t = arr[i];
+/// // use t, do not use i
+/// }
+/// \endcode
+/// and
+/// \code
+/// for (iterator i = container.begin(), e = container.end(); i != e; ++i) {
+/// T t = *i;
+/// // use t, do not use i
+/// }
+/// \endcode
+static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
+ const VarDecl *IndexVar) {
+ const auto *VDecl = dyn_cast<VarDecl>(TheDecl);
+ if (!VDecl)
+ return false;
+ if (!VDecl->hasInit())
+ return false;
+
+ bool OnlyCasts = true;
+ const Expr *Init = VDecl->getInit()->IgnoreParenImpCasts();
+ if (Init && isa<CXXConstructExpr>(Init)) {
+ Init = digThroughConstructors(Init);
+ OnlyCasts = false;
+ }
+ if (!Init)
+ return false;
+
+ // Check that the declared type is the same as (or a reference to) the
+ // container type.
+ if (!OnlyCasts) {
+ QualType InitType = Init->getType();
+ QualType DeclarationType = VDecl->getType();
+ if (!DeclarationType.isNull() && DeclarationType->isReferenceType())
+ DeclarationType = DeclarationType.getNonReferenceType();
+
+ if (InitType.isNull() || DeclarationType.isNull() ||
+ !Context->hasSameUnqualifiedType(DeclarationType, InitType))
+ return false;
+ }
+
+ switch (Init->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass: {
+ const auto *E = cast<ArraySubscriptExpr>(Init);
+ // We don't really care which array is used here. We check to make sure
+ // it was the correct one later, since the AST will traverse it next.
+ return isIndexInSubscriptExpr(E->getIdx(), IndexVar);
+ }
+
+ case Stmt::UnaryOperatorClass:
+ return isDereferenceOfUop(cast<UnaryOperator>(Init), IndexVar);
+
+ case Stmt::CXXOperatorCallExprClass: {
+ const auto *OpCall = cast<CXXOperatorCallExpr>(Init);
+ if (OpCall->getOperator() == OO_Star)
+ return isDereferenceOfOpCall(OpCall, IndexVar);
+ if (OpCall->getOperator() == OO_Subscript) {
+ assert(OpCall->getNumArgs() == 2);
+ return isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
+ }
+ break;
+ }
+
+ case Stmt::CXXMemberCallExprClass: {
+ const auto *MemCall = cast<CXXMemberCallExpr>(Init);
+ // This check is needed because getMethodDecl can return nullptr if the
+ // callee is a member function pointer.
+ const auto *MDecl = MemCall->getMethodDecl();
+ if (MDecl && !isa<CXXConversionDecl>(MDecl) &&
+ MDecl->getNameAsString() == "at" && MemCall->getNumArgs() == 1) {
+ return isIndexInSubscriptExpr(MemCall->getArg(0), IndexVar);
+ }
+ return false;
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/// \brief Determines whether the bound of a for loop condition expression is
+/// the same as the statically computable size of ArrayType.
+///
+/// Given
+/// \code
+/// const int N = 5;
+/// int arr[N];
+/// \endcode
+/// This is intended to permit
+/// \code
+/// for (int i = 0; i < N; ++i) { /* use arr[i] */ }
+/// for (int i = 0; i < arraysize(arr); ++i) { /* use arr[i] */ }
+/// \endcode
+static bool arrayMatchesBoundExpr(ASTContext *Context,
+ const QualType &ArrayType,
+ const Expr *ConditionExpr) {
+ if (!ConditionExpr || ConditionExpr->isValueDependent())
+ return false;
+ const ConstantArrayType *ConstType =
+ Context->getAsConstantArrayType(ArrayType);
+ if (!ConstType)
+ return false;
+ llvm::APSInt ConditionSize;
+ if (!ConditionExpr->isIntegerConstantExpr(ConditionSize, *Context))
+ return false;
+ llvm::APSInt ArraySize(ConstType->getSize());
+ return llvm::APSInt::isSameValue(ConditionSize, ArraySize);
+}
+
+ForLoopIndexUseVisitor::ForLoopIndexUseVisitor(ASTContext *Context,
+ const VarDecl *IndexVar,
+ const VarDecl *EndVar,
+ const Expr *ContainerExpr,
+ const Expr *ArrayBoundExpr,
+ bool ContainerNeedsDereference)
+ : Context(Context), IndexVar(IndexVar), EndVar(EndVar),
+ ContainerExpr(ContainerExpr), ArrayBoundExpr(ArrayBoundExpr),
+ ContainerNeedsDereference(ContainerNeedsDereference),
+ OnlyUsedAsIndex(true), AliasDecl(nullptr),
+ ConfidenceLevel(Confidence::CL_Safe), NextStmtParent(nullptr),
+ CurrStmtParent(nullptr), ReplaceWithAliasUse(false),
+ AliasFromForInit(false) {
+ if (ContainerExpr)
+ addComponent(ContainerExpr);
+}
+
+bool ForLoopIndexUseVisitor::findAndVerifyUsages(const Stmt *Body) {
+ TraverseStmt(const_cast<Stmt *>(Body));
+ return OnlyUsedAsIndex && ContainerExpr;
+}
+
+void ForLoopIndexUseVisitor::addComponents(const ComponentVector &Components) {
+ // FIXME: add sort(on ID)+unique to avoid extra work.
+ for (const auto &I : Components)
+ addComponent(I);
+}
+
+void ForLoopIndexUseVisitor::addComponent(const Expr *E) {
+ llvm::FoldingSetNodeID ID;
+ const Expr *Node = E->IgnoreParenImpCasts();
+ Node->Profile(ID, *Context, true);
+ DependentExprs.push_back(std::make_pair(Node, ID));
+}
+
+void ForLoopIndexUseVisitor::addUsage(const Usage &U) {
+ SourceLocation Begin = U.Range.getBegin();
+ if (Begin.isMacroID())
+ Begin = Context->getSourceManager().getSpellingLoc(Begin);
+
+ if (UsageLocations.insert(Begin).second)
+ Usages.push_back(U);
+}
+
+/// \brief If the unary operator is a dereference of IndexVar, include it
+/// as a valid usage and prune the traversal.
+///
+/// For example, if container.begin() and container.end() both return pointers
+/// to int, this makes sure that the initialization for `k` is not counted as an
+/// unconvertible use of the iterator `i`.
+/// \code
+/// for (int *i = container.begin(), *e = container.end(); i != e; ++i) {
+/// int k = *i + 2;
+/// }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseUnaryDeref(UnaryOperator *Uop) {
+ // If we dereference an iterator that's actually a pointer, count the
+ // occurrence.
+ if (isDereferenceOfUop(Uop, IndexVar)) {
+ addUsage(Usage(Uop));
+ return true;
+ }
+
+ return VisitorBase::TraverseUnaryOperator(Uop);
+}
+
+/// \brief If the member expression is operator-> (overloaded or not) on
+/// IndexVar, include it as a valid usage and prune the traversal.
+///
+/// For example, given
+/// \code
+/// struct Foo { int bar(); int x; };
+/// vector<Foo> v;
+/// \endcode
+/// the following uses will be considered convertible:
+/// \code
+/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+/// int b = i->bar();
+/// int k = i->x + 1;
+/// }
+/// \endcode
+/// though
+/// \code
+/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+/// int k = i.insert(1);
+/// }
+/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+/// int b = e->bar();
+/// }
+/// \endcode
+/// will not.
+bool ForLoopIndexUseVisitor::TraverseMemberExpr(MemberExpr *Member) {
+ const Expr *Base = Member->getBase();
+ const DeclRefExpr *Obj = getDeclRef(Base);
+ const Expr *ResultExpr = Member;
+ QualType ExprType;
+ if (const auto *Call =
+ dyn_cast<CXXOperatorCallExpr>(Base->IgnoreParenImpCasts())) {
+ // If operator->() is a MemberExpr containing a CXXOperatorCallExpr, then
+ // the MemberExpr does not have the expression we want. We therefore catch
+ // that instance here.
+ // For example, if vector<Foo>::iterator defines operator->(), then the
+ // example `i->bar()` at the top of this function is a CXXMemberCallExpr
+ // referring to `i->` as the member function called. We want just `i`, so
+ // we take the argument to operator->() as the base object.
+ if (Call->getOperator() == OO_Arrow) {
+ assert(Call->getNumArgs() == 1 &&
+ "Operator-> takes more than one argument");
+ Obj = getDeclRef(Call->getArg(0));
+ ResultExpr = Obj;
+ ExprType = Call->getCallReturnType(*Context);
+ }
+ }
+
+ if (Obj && exprReferencesVariable(IndexVar, Obj)) {
+ // Member calls on the iterator with '.' are not allowed.
+ if (!Member->isArrow()) {
+ OnlyUsedAsIndex = false;
+ return true;
+ }
+
+ if (ExprType.isNull())
+ ExprType = Obj->getType();
+
+ if (!ExprType->isPointerType())
+ return false;
+
+ // FIXME: This works around not having the location of the arrow operator.
+ // Consider adding OperatorLoc to MemberExpr?
+ SourceLocation ArrowLoc = Lexer::getLocForEndOfToken(
+ Base->getExprLoc(), 0, Context->getSourceManager(),
+ Context->getLangOpts());
+ // If something complicated is happening (i.e. the next token isn't an
+ // arrow), give up on making this work.
+ if (ArrowLoc.isValid()) {
+ addUsage(Usage(ResultExpr, Usage::UK_MemberThroughArrow,
+ SourceRange(Base->getExprLoc(), ArrowLoc)));
+ return true;
+ }
+ }
+ return VisitorBase::TraverseMemberExpr(Member);
+}
+
+/// \brief If a member function call is the at() accessor on the container with
+/// IndexVar as the single argument, include it as a valid usage and prune
+/// the traversal.
+///
+/// Member calls on other objects will not be permitted.
+/// Calls on the iterator object are not permitted, unless done through
+/// operator->(). The one exception is allowing vector::at() for pseudoarrays.
+bool ForLoopIndexUseVisitor::TraverseCXXMemberCallExpr(
+ CXXMemberCallExpr *MemberCall) {
+ auto *Member =
+ dyn_cast<MemberExpr>(MemberCall->getCallee()->IgnoreParenImpCasts());
+ if (!Member)
+ return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
+
+ // We specifically allow an accessor named "at" to let STL in, though
+ // this is restricted to pseudo-arrays by requiring a single, integer
+ // argument.
+ const IdentifierInfo *Ident = Member->getMemberDecl()->getIdentifier();
+ if (Ident && Ident->isStr("at") && MemberCall->getNumArgs() == 1) {
+ if (isIndexInSubscriptExpr(Context, MemberCall->getArg(0), IndexVar,
+ Member->getBase(), ContainerExpr,
+ ContainerNeedsDereference)) {
+ addUsage(Usage(MemberCall));
+ return true;
+ }
+ }
+
+ if (containsExpr(Context, &DependentExprs, Member->getBase()))
+ ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+
+ return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
+}
+
+/// \brief If an overloaded operator call is a dereference of IndexVar or
+/// a subscript of the container with IndexVar as the single argument,
+/// include it as a valid usage and prune the traversal.
+///
+/// For example, given
+/// \code
+/// struct Foo { int bar(); int x; };
+/// vector<Foo> v;
+/// void f(Foo);
+/// \endcode
+/// the following uses will be considered convertible:
+/// \code
+/// for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+/// f(*i);
+/// }
+/// for (int i = 0; i < v.size(); ++i) {
+/// int i = v[i] + 1;
+/// }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseCXXOperatorCallExpr(
+ CXXOperatorCallExpr *OpCall) {
+ switch (OpCall->getOperator()) {
+ case OO_Star:
+ if (isDereferenceOfOpCall(OpCall, IndexVar)) {
+ addUsage(Usage(OpCall));
+ return true;
+ }
+ break;
+
+ case OO_Subscript:
+ if (OpCall->getNumArgs() != 2)
+ break;
+ if (isIndexInSubscriptExpr(Context, OpCall->getArg(1), IndexVar,
+ OpCall->getArg(0), ContainerExpr,
+ ContainerNeedsDereference)) {
+ addUsage(Usage(OpCall));
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return VisitorBase::TraverseCXXOperatorCallExpr(OpCall);
+}
+
+/// \brief If we encounter an array with IndexVar as the index of an
+/// ArraySubsriptExpression, note it as a consistent usage and prune the
+/// AST traversal.
+///
+/// For example, given
+/// \code
+/// const int N = 5;
+/// int arr[N];
+/// \endcode
+/// This is intended to permit
+/// \code
+/// for (int i = 0; i < N; ++i) { /* use arr[i] */ }
+/// \endcode
+/// but not
+/// \code
+/// for (int i = 0; i < N; ++i) { /* use notArr[i] */ }
+/// \endcode
+/// and further checking needs to be done later to ensure that exactly one array
+/// is referenced.
+bool ForLoopIndexUseVisitor::TraverseArraySubscriptExpr(ArraySubscriptExpr *E) {
+ Expr *Arr = E->getBase();
+ if (!isIndexInSubscriptExpr(E->getIdx(), IndexVar))
+ return VisitorBase::TraverseArraySubscriptExpr(E);
+
+ if ((ContainerExpr &&
+ !areSameExpr(Context, Arr->IgnoreParenImpCasts(),
+ ContainerExpr->IgnoreParenImpCasts())) ||
+ !arrayMatchesBoundExpr(Context, Arr->IgnoreImpCasts()->getType(),
+ ArrayBoundExpr)) {
+ // If we have already discovered the array being indexed and this isn't it
+ // or this array doesn't match, mark this loop as unconvertible.
+ OnlyUsedAsIndex = false;
+ return VisitorBase::TraverseArraySubscriptExpr(E);
+ }
+
+ if (!ContainerExpr)
+ ContainerExpr = Arr;
+
+ addUsage(Usage(E));
+ return true;
+}
+
+/// \brief If we encounter a reference to IndexVar in an unpruned branch of the
+/// traversal, mark this loop as unconvertible.
+///
+/// This implements the whitelist for convertible loops: any usages of IndexVar
+/// not explicitly considered convertible by this traversal will be caught by
+/// this function.
+///
+/// Additionally, if the container expression is more complex than just a
+/// DeclRefExpr, and some part of it is appears elsewhere in the loop, lower
+/// our confidence in the transformation.
+///
+/// For example, these are not permitted:
+/// \code
+/// for (int i = 0; i < N; ++i) { printf("arr[%d] = %d", i, arr[i]); }
+/// for (vector<int>::iterator i = container.begin(), e = container.end();
+/// i != e; ++i)
+/// i.insert(0);
+/// for (vector<int>::iterator i = container.begin(), e = container.end();
+/// i != e; ++i)
+/// if (i + 1 != e)
+/// printf("%d", *i);
+/// \endcode
+///
+/// And these will raise the risk level:
+/// \code
+/// int arr[10][20];
+/// int l = 5;
+/// for (int j = 0; j < 20; ++j)
+/// int k = arr[l][j] + l; // using l outside arr[l] is considered risky
+/// for (int i = 0; i < obj.getVector().size(); ++i)
+/// obj.foo(10); // using `obj` is considered risky
+/// \endcode
+bool ForLoopIndexUseVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
+ const ValueDecl *TheDecl = E->getDecl();
+ if (areSameVariable(IndexVar, TheDecl) ||
+ exprReferencesVariable(IndexVar, E) || areSameVariable(EndVar, TheDecl) ||
+ exprReferencesVariable(EndVar, E))
+ OnlyUsedAsIndex = false;
+ if (containsExpr(Context, &DependentExprs, E))
+ ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+ return true;
+}
+
+/// \brief If the loop index is captured by a lambda, replace this capture
+/// by the range-for loop variable.
+///
+/// For example:
+/// \code
+/// for (int i = 0; i < N; ++i) {
+/// auto f = [v, i](int k) {
+/// printf("%d\n", v[i] + k);
+/// };
+/// f(v[i]);
+/// }
+/// \endcode
+///
+/// Will be replaced by:
+/// \code
+/// for (auto & elem : v) {
+/// auto f = [v, elem](int k) {
+/// printf("%d\n", elem + k);
+/// };
+/// f(elem);
+/// }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
+ const LambdaCapture *C,
+ Expr *Init) {
+ if (C->capturesVariable()) {
+ const VarDecl *VDecl = C->getCapturedVar();
+ if (areSameVariable(IndexVar, cast<ValueDecl>(VDecl))) {
+ // FIXME: if the index is captured, it will count as an usage and the
+ // alias (if any) won't work, because it is only used in case of having
+ // exactly one usage.
+ addUsage(Usage(nullptr,
+ C->getCaptureKind() == LCK_ByCopy ? Usage::UK_CaptureByCopy
+ : Usage::UK_CaptureByRef,
+ C->getLocation()));
+ }
+ }
+ return VisitorBase::TraverseLambdaCapture(LE, C, Init);
+}
+
+/// \brief If we find that another variable is created just to refer to the loop
+/// element, note it for reuse as the loop variable.
+///
+/// See the comments for isAliasDecl.
+bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) {
+ if (!AliasDecl && S->isSingleDecl() &&
+ isAliasDecl(Context, S->getSingleDecl(), IndexVar)) {
+ AliasDecl = S;
+ if (CurrStmtParent) {
+ if (isa<IfStmt>(CurrStmtParent) || isa<WhileStmt>(CurrStmtParent) ||
+ isa<SwitchStmt>(CurrStmtParent))
+ ReplaceWithAliasUse = true;
+ else if (isa<ForStmt>(CurrStmtParent)) {
+ if (cast<ForStmt>(CurrStmtParent)->getConditionVariableDeclStmt() == S)
+ ReplaceWithAliasUse = true;
+ else
+ // It's assumed S came the for loop's init clause.
+ AliasFromForInit = true;
+ }
+ }
+ }
+
+ return true;
+}
+
+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<LambdaExpr>(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;
+ CurrStmtParent = NextStmtParent;
+ NextStmtParent = S;
+ bool Result = VisitorBase::TraverseStmt(S);
+ NextStmtParent = OldNextParent;
+ return Result;
+}
+
+std::string VariableNamer::createIndexName() {
+ // FIXME: Add in naming conventions to handle:
+ // - How to handle conflicts.
+ // - An interactive process for naming.
+ std::string IteratorName;
+ StringRef ContainerName;
+ if (TheContainer)
+ ContainerName = TheContainer->getName();
+
+ size_t Len = ContainerName.size();
+ if (Len > 1 && ContainerName.endswith(Style == NS_UpperCase ? "S" : "s")) {
+ IteratorName = ContainerName.substr(0, Len - 1);
+ // E.g.: (auto thing : things)
+ if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
+ return IteratorName;
+ }
+
+ if (Len > 2 && ContainerName.endswith(Style == NS_UpperCase ? "S_" : "s_")) {
+ IteratorName = ContainerName.substr(0, Len - 2);
+ // E.g.: (auto thing : things_)
+ if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
+ return IteratorName;
+ }
+
+ return OldIndex->getName();
+}
+
+/// \brief Determines whether or not the the name \a Symbol conflicts with
+/// language keywords or defined macros. Also checks if the name exists in
+/// LoopContext, any of its parent contexts, or any of its child statements.
+///
+/// We also check to see if the same identifier was generated by this loop
+/// converter in a loop nested within SourceStmt.
+bool VariableNamer::declarationExists(StringRef Symbol) {
+ assert(Context != nullptr && "Expected an ASTContext");
+ IdentifierInfo &Ident = Context->Idents.get(Symbol);
+
+ // Check if the symbol is not an identifier (ie. is a keyword or alias).
+ if (!isAnyIdentifier(Ident.getTokenID()))
+ return true;
+
+ // Check for conflicting macro definitions.
+ if (Ident.hasMacroDefinition())
+ return true;
+
+ // Determine if the symbol was generated in a parent context.
+ for (const Stmt *S = SourceStmt; S != nullptr; S = ReverseAST->lookup(S)) {
+ StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
+ if (I != GeneratedDecls->end() && I->second == Symbol)
+ return true;
+ }
+
+ // FIXME: Rather than detecting conflicts at their usages, we should check the
+ // parent context.
+ // For some reason, lookup() always returns the pair (NULL, NULL) because its
+ // StoredDeclsMap is not initialized (i.e. LookupPtr.getInt() is false inside
+ // of DeclContext::lookup()). Why is this?
+
+ // Finally, determine if the symbol was used in the loop or a child context.
+ DeclFinderASTVisitor DeclFinder(Symbol, GeneratedDecls);
+ return DeclFinder.findUsages(SourceStmt);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- LoopConvertUtils.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_MODERNIZE_LOOP_CONVERT_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+enum LoopFixerKind { LFK_Array, LFK_Iterator, LFK_PseudoArray };
+
+/// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
+typedef llvm::DenseMap<const clang::Stmt *, const clang::Stmt *> StmtParentMap;
+
+/// A map used to walk the AST in reverse:
+/// maps VarDecl to the to parent DeclStmt.
+typedef llvm::DenseMap<const clang::VarDecl *, const clang::DeclStmt *>
+ DeclParentMap;
+
+/// A map used to track which variables have been removed by a refactoring pass.
+/// It maps the parent ForStmt to the removed index variable's VarDecl.
+typedef llvm::DenseMap<const clang::ForStmt *, const clang::VarDecl *>
+ ReplacedVarsMap;
+
+/// A map used to remember the variable names generated in a Stmt
+typedef llvm::DenseMap<const clang::Stmt *, std::string>
+ StmtGeneratedVarNameMap;
+
+/// A vector used to store the AST subtrees of an Expr.
+typedef llvm::SmallVector<const clang::Expr *, 16> ComponentVector;
+
+/// \brief Class used build the reverse AST properties needed to detect
+/// name conflicts and free variables.
+class StmtAncestorASTVisitor
+ : public clang::RecursiveASTVisitor<StmtAncestorASTVisitor> {
+public:
+ StmtAncestorASTVisitor() { StmtStack.push_back(nullptr); }
+
+ /// \brief Run the analysis on the TranslationUnitDecl.
+ ///
+ /// In case we're running this analysis multiple times, don't repeat the work.
+ void gatherAncestors(const clang::TranslationUnitDecl *T) {
+ if (StmtAncestors.empty())
+ TraverseDecl(const_cast<clang::TranslationUnitDecl *>(T));
+ }
+
+ /// Accessor for StmtAncestors.
+ const StmtParentMap &getStmtToParentStmtMap() { return StmtAncestors; }
+
+ /// Accessor for DeclParents.
+ const DeclParentMap &getDeclToParentStmtMap() { return DeclParents; }
+
+ friend class clang::RecursiveASTVisitor<StmtAncestorASTVisitor>;
+
+private:
+ StmtParentMap StmtAncestors;
+ DeclParentMap DeclParents;
+ llvm::SmallVector<const clang::Stmt *, 16> StmtStack;
+
+ bool TraverseStmt(clang::Stmt *Statement);
+ bool VisitDeclStmt(clang::DeclStmt *Statement);
+};
+
+/// Class used to find the variables and member expressions on which an
+/// arbitrary expression depends.
+class ComponentFinderASTVisitor
+ : public clang::RecursiveASTVisitor<ComponentFinderASTVisitor> {
+public:
+ ComponentFinderASTVisitor() = default;
+
+ /// Find the components of an expression and place them in a ComponentVector.
+ void findExprComponents(const clang::Expr *SourceExpr) {
+ TraverseStmt(const_cast<clang::Expr *>(SourceExpr));
+ }
+
+ /// Accessor for Components.
+ const ComponentVector &getComponents() { return Components; }
+
+ friend class clang::RecursiveASTVisitor<ComponentFinderASTVisitor>;
+
+private:
+ ComponentVector Components;
+
+ bool VisitDeclRefExpr(clang::DeclRefExpr *E);
+ bool VisitMemberExpr(clang::MemberExpr *Member);
+};
+
+/// Class used to determine if an expression is dependent on a variable declared
+/// inside of the loop where it would be used.
+class DependencyFinderASTVisitor
+ : public clang::RecursiveASTVisitor<DependencyFinderASTVisitor> {
+public:
+ DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
+ const DeclParentMap *DeclParents,
+ const ReplacedVarsMap *ReplacedVars,
+ const clang::Stmt *ContainingStmt)
+ : StmtParents(StmtParents), DeclParents(DeclParents),
+ ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) {}
+
+ /// \brief Run the analysis on Body, and return true iff the expression
+ /// depends on some variable declared within ContainingStmt.
+ ///
+ /// This is intended to protect against hoisting the container expression
+ /// outside of an inner context if part of that expression is declared in that
+ /// inner context.
+ ///
+ /// For example,
+ /// \code
+ /// const int N = 10, M = 20;
+ /// int arr[N][M];
+ /// int getRow();
+ ///
+ /// for (int i = 0; i < M; ++i) {
+ /// int k = getRow();
+ /// printf("%d:", arr[k][i]);
+ /// }
+ /// \endcode
+ /// At first glance, this loop looks like it could be changed to
+ /// \code
+ /// for (int elem : arr[k]) {
+ /// int k = getIndex();
+ /// printf("%d:", elem);
+ /// }
+ /// \endcode
+ /// But this is malformed, since `k` is used before it is defined!
+ ///
+ /// In order to avoid this, this class looks at the container expression
+ /// `arr[k]` and decides whether or not it contains a sub-expression declared
+ /// within the the loop body.
+ bool dependsOnInsideVariable(const clang::Stmt *Body) {
+ DependsOnInsideVariable = false;
+ TraverseStmt(const_cast<clang::Stmt *>(Body));
+ return DependsOnInsideVariable;
+ }
+
+ friend class clang::RecursiveASTVisitor<DependencyFinderASTVisitor>;
+
+private:
+ const StmtParentMap *StmtParents;
+ const DeclParentMap *DeclParents;
+ const clang::Stmt *ContainingStmt;
+ const ReplacedVarsMap *ReplacedVars;
+ bool DependsOnInsideVariable;
+
+ bool VisitVarDecl(clang::VarDecl *V);
+ bool VisitDeclRefExpr(clang::DeclRefExpr *D);
+};
+
+/// Class used to determine if any declarations used in a Stmt would conflict
+/// with a particular identifier. This search includes the names that don't
+/// actually appear in the AST (i.e. created by a refactoring tool) by including
+/// a map from Stmts to generated names associated with those stmts.
+class DeclFinderASTVisitor
+ : public clang::RecursiveASTVisitor<DeclFinderASTVisitor> {
+public:
+ DeclFinderASTVisitor(const std::string &Name,
+ const StmtGeneratedVarNameMap *GeneratedDecls)
+ : Name(Name), GeneratedDecls(GeneratedDecls), Found(false) {}
+
+ /// Attempts to find any usages of variables name Name in Body, returning
+ /// true when it is used in Body. This includes the generated loop variables
+ /// of ForStmts which have already been transformed.
+ bool findUsages(const clang::Stmt *Body) {
+ Found = false;
+ TraverseStmt(const_cast<clang::Stmt *>(Body));
+ return Found;
+ }
+
+ friend class clang::RecursiveASTVisitor<DeclFinderASTVisitor>;
+
+private:
+ std::string Name;
+ /// GeneratedDecls keeps track of ForStmts which have been transformed,
+ /// mapping each modified ForStmt to the variable generated in the loop.
+ const StmtGeneratedVarNameMap *GeneratedDecls;
+ bool Found;
+
+ bool VisitForStmt(clang::ForStmt *F);
+ bool VisitNamedDecl(clang::NamedDecl *D);
+ bool VisitDeclRefExpr(clang::DeclRefExpr *D);
+ bool VisitTypeLoc(clang::TypeLoc TL);
+};
+
+/// \brief The information needed to describe a valid convertible usage
+/// of an array index or iterator.
+struct Usage {
+ enum UsageKind {
+ // Regular usages of the loop index (the ones not specified below). Some
+ // examples:
+ // \code
+ // int X = 8 * Arr[i];
+ // ^~~~~~
+ // f(param1, param2, *It);
+ // ^~~
+ // if (Vec[i].SomeBool) {}
+ // ^~~~~~
+ // \endcode
+ UK_Default,
+ // Indicates whether this is an access to a member through the arrow
+ // operator on pointers or iterators.
+ UK_MemberThroughArrow,
+ // If the variable is being captured by a lambda, indicates whether the
+ // capture was done by value or by reference.
+ UK_CaptureByCopy,
+ UK_CaptureByRef
+ };
+ // The expression that is going to be converted. Null in case of lambda
+ // captures.
+ const Expr *Expression;
+
+ UsageKind Kind;
+
+ // Range that covers this usage.
+ SourceRange Range;
+
+ explicit Usage(const Expr *E)
+ : Expression(E), Kind(UK_Default), Range(Expression->getSourceRange()) {}
+ Usage(const Expr *E, UsageKind Kind, SourceRange Range)
+ : Expression(E), Kind(Kind), Range(std::move(Range)) {}
+};
+
+/// \brief A class to encapsulate lowering of the tool's confidence level.
+class Confidence {
+public:
+ enum Level {
+ // Transformations that are likely to change semantics.
+ CL_Risky,
+
+ // Transformations that might change semantics.
+ CL_Reasonable,
+
+ // Transformations that will not change semantics.
+ CL_Safe
+ };
+ /// \brief Initialize confidence level.
+ explicit Confidence(Confidence::Level Level) : CurrentLevel(Level) {}
+
+ /// \brief Lower the internal confidence level to Level, but do not raise it.
+ void lowerTo(Confidence::Level Level) {
+ CurrentLevel = std::min(Level, CurrentLevel);
+ }
+
+ /// \brief Return the internal confidence level.
+ Level getLevel() const { return CurrentLevel; }
+
+private:
+ Level CurrentLevel;
+};
+
+// The main computational result of ForLoopIndexVisitor.
+typedef llvm::SmallVector<Usage, 8> UsageResult;
+
+// General functions used by ForLoopIndexUseVisitor and LoopConvertCheck.
+const Expr *digThroughConstructors(const Expr *E);
+bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second);
+const DeclRefExpr *getDeclRef(const Expr *E);
+bool areSameVariable(const ValueDecl *First, const ValueDecl *Second);
+
+/// \brief Discover usages of expressions consisting of index or iterator
+/// access.
+///
+/// Given an index variable, recursively crawls a for loop to discover if the
+/// index variable is used in a way consistent with range-based for loop access.
+class ForLoopIndexUseVisitor
+ : public RecursiveASTVisitor<ForLoopIndexUseVisitor> {
+public:
+ ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar,
+ const VarDecl *EndVar, const Expr *ContainerExpr,
+ const Expr *ArrayBoundExpr,
+ bool ContainerNeedsDereference);
+
+ /// \brief Finds all uses of IndexVar in Body, placing all usages in Usages,
+ /// and returns true if IndexVar was only used in a way consistent with a
+ /// range-based for loop.
+ ///
+ /// The general strategy is to reject any DeclRefExprs referencing IndexVar,
+ /// with the exception of certain acceptable patterns.
+ /// For arrays, the DeclRefExpr for IndexVar must appear as the index of an
+ /// ArraySubscriptExpression. Iterator-based loops may dereference
+ /// IndexVar or call methods through operator-> (builtin or overloaded).
+ /// Array-like containers may use IndexVar as a parameter to the at() member
+ /// function and in overloaded operator[].
+ bool findAndVerifyUsages(const Stmt *Body);
+
+ /// \brief Add a set of components that we should consider relevant to the
+ /// container.
+ void addComponents(const ComponentVector &Components);
+
+ /// \brief Accessor for Usages.
+ const UsageResult &getUsages() const { return Usages; }
+
+ /// \brief Adds the Usage if it was not added before.
+ void addUsage(const Usage &U);
+
+ /// \brief Get the container indexed by IndexVar, if any.
+ const Expr *getContainerIndexed() const { return ContainerExpr; }
+
+ /// \brief Returns the statement declaring the variable created as an alias
+ /// for the loop element, if any.
+ const DeclStmt *getAliasDecl() const { return AliasDecl; }
+
+ /// \brief Accessor for ConfidenceLevel.
+ Confidence::Level getConfidenceLevel() const {
+ return ConfidenceLevel.getLevel();
+ }
+
+ /// \brief Indicates if the alias declaration was in a place where it cannot
+ /// simply be removed but rather replaced with a use of the alias variable.
+ /// For example, variables declared in the condition of an if, switch, or for
+ /// stmt.
+ bool aliasUseRequired() const { return ReplaceWithAliasUse; }
+
+ /// \brief Indicates if the alias declaration came from the init clause of a
+ /// nested for loop. SourceRanges provided by Clang for DeclStmts in this
+ /// case need to be adjusted.
+ bool aliasFromForInit() const { return AliasFromForInit; }
+
+private:
+ /// Typedef used in CRTP functions.
+ typedef RecursiveASTVisitor<ForLoopIndexUseVisitor> VisitorBase;
+ friend class RecursiveASTVisitor<ForLoopIndexUseVisitor>;
+
+ /// Overriden methods for RecursiveASTVisitor's traversal.
+ bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E);
+ bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
+ bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
+ bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+ Expr *Init);
+ bool TraverseMemberExpr(MemberExpr *Member);
+ bool TraverseUnaryDeref(UnaryOperator *Uop);
+ bool VisitDeclRefExpr(DeclRefExpr *E);
+ bool VisitDeclStmt(DeclStmt *S);
+ bool TraverseStmt(Stmt *S);
+
+ /// \brief Add an expression to the list of expressions on which the container
+ /// expression depends.
+ void addComponent(const Expr *E);
+
+ // Input member variables:
+ ASTContext *Context;
+ /// The index variable's VarDecl.
+ const VarDecl *IndexVar;
+ /// The loop's 'end' variable, which cannot be mentioned at all.
+ const VarDecl *EndVar;
+ /// The Expr which refers to the container.
+ const Expr *ContainerExpr;
+ /// The Expr which refers to the terminating condition for array-based loops.
+ const Expr *ArrayBoundExpr;
+ bool ContainerNeedsDereference;
+
+ // Output member variables:
+ /// A container which holds all usages of IndexVar as the index of
+ /// ArraySubscriptExpressions.
+ UsageResult Usages;
+ llvm::SmallSet<SourceLocation, 8> UsageLocations;
+ bool OnlyUsedAsIndex;
+ /// The DeclStmt for an alias to the container element.
+ const DeclStmt *AliasDecl;
+ Confidence ConfidenceLevel;
+ /// \brief A list of expressions on which ContainerExpr depends.
+ ///
+ /// If any of these expressions are encountered outside of an acceptable usage
+ /// of the loop element, lower our confidence level.
+ llvm::SmallVector<std::pair<const Expr *, llvm::FoldingSetNodeID>, 16>
+ DependentExprs;
+
+ /// The parent-in-waiting. Will become the real parent once we traverse down
+ /// one level in the AST.
+ const Stmt *NextStmtParent;
+ /// The actual parent of a node when Visit*() calls are made. Only the
+ /// parentage of DeclStmt's to possible iteration/selection statements is of
+ /// importance.
+ const Stmt *CurrStmtParent;
+
+ /// \see aliasUseRequired().
+ bool ReplaceWithAliasUse;
+ /// \see aliasFromForInit().
+ bool AliasFromForInit;
+};
+
+struct TUTrackingInfo {
+ /// \brief Reset and initialize per-TU tracking information.
+ ///
+ /// Must be called before using container accessors.
+ TUTrackingInfo() : ParentFinder(new StmtAncestorASTVisitor) {}
+
+ StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
+ StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
+ ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
+
+private:
+ std::unique_ptr<StmtAncestorASTVisitor> ParentFinder;
+ StmtGeneratedVarNameMap GeneratedDecls;
+ ReplacedVarsMap ReplacedVars;
+};
+
+/// \brief Create names for generated variables within a particular statement.
+///
+/// VariableNamer uses a DeclContext as a reference point, checking for any
+/// conflicting declarations higher up in the context or within SourceStmt.
+/// It creates a variable name using hints from a source container and the old
+/// index, if they exist.
+class VariableNamer {
+public:
+ // Supported naming styles.
+ enum NamingStyle {
+ NS_CamelBack,
+ NS_CamelCase,
+ NS_LowerCase,
+ NS_UpperCase,
+ };
+
+ VariableNamer(StmtGeneratedVarNameMap *GeneratedDecls,
+ const StmtParentMap *ReverseAST, const clang::Stmt *SourceStmt,
+ const clang::VarDecl *OldIndex,
+ const clang::ValueDecl *TheContainer,
+ const clang::ASTContext *Context, NamingStyle Style)
+ : GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
+ SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
+ Context(Context), Style(Style) {}
+
+ /// \brief Generate a new index name.
+ ///
+ /// Generates the name to be used for an inserted iterator. It relies on
+ /// declarationExists() to determine that there are no naming conflicts, and
+ /// tries to use some hints from the container name and the old index name.
+ std::string createIndexName();
+
+private:
+ StmtGeneratedVarNameMap *GeneratedDecls;
+ const StmtParentMap *ReverseAST;
+ const clang::Stmt *SourceStmt;
+ const clang::VarDecl *OldIndex;
+ const clang::ValueDecl *TheContainer;
+ const clang::ASTContext *Context;
+ const NamingStyle Style;
+
+ // Determine whether or not a declaration that would conflict with Symbol
+ // exists in an outer context or in any statement contained in SourceStmt.
+ bool declarationExists(llvm::StringRef Symbol);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
--- /dev/null
+//===--- MakeSharedCheck.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 "MakeSharedCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+MakeSharedCheck::MakeSharedCheck(StringRef Name, ClangTidyContext *Context)
+ : MakeSmartPtrCheck(Name, Context, "std::make_shared") {}
+
+MakeSharedCheck::SmartPtrTypeMatcher
+MakeSharedCheck::getSmartPointerTypeMatcher() const {
+ return qualType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("::std::shared_ptr"), templateArgumentCountIs(1),
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(qualType().bind(PointerType)))))));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MakeSharedCheck.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_MODERNIZE_MAKE_SHARED_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
+
+#include "MakeSmartPtrCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace the pattern:
+/// \code
+/// std::shared_ptr<type>(new type(args...))
+/// \endcode
+///
+/// With the safer version:
+/// \code
+/// std::make_shared<type>(args...)
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html
+class MakeSharedCheck : public MakeSmartPtrCheck {
+public:
+ MakeSharedCheck(StringRef Name, ClangTidyContext *Context);
+
+protected:
+ SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
--- /dev/null
+//===--- MakeSmartPtrCheck.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 "MakeSharedCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+constexpr char StdMemoryHeader[] = "memory";
+
+std::string GetNewExprName(const CXXNewExpr *NewExpr,
+ const SourceManager &SM,
+ const LangOptions &Lang) {
+ StringRef WrittenName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
+ SM, Lang);
+ if (NewExpr->isArray()) {
+ return WrittenName.str() + "[]";
+ }
+ return WrittenName.str();
+}
+
+} // namespace
+
+const char MakeSmartPtrCheck::PointerType[] = "pointerType";
+const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
+const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
+const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
+
+MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
+ StringRef MakeSmartPtrFunctionName)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))),
+ MakeSmartPtrFunctionHeader(
+ Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
+ MakeSmartPtrFunctionName(
+ Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)) {}
+
+void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle", IncludeStyle);
+ Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
+ Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
+}
+
+void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ if (getLangOpts().CPlusPlus11) {
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+ }
+}
+
+void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // Calling make_smart_ptr from within a member function of a type with a
+ // private or protected constructor would be ill-formed.
+ auto CanCallCtor = unless(has(ignoringImpCasts(
+ cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
+
+ Finder->addMatcher(
+ cxxBindTemporaryExpr(has(ignoringParenImpCasts(
+ cxxConstructExpr(
+ hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
+ hasArgument(0,
+ cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
+ equalsBoundNode(PointerType))))),
+ CanCallCtor)
+ .bind(NewExpression)))
+ .bind(ConstructorCall)))),
+ this);
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ thisPointerType(getSmartPointerTypeMatcher()),
+ callee(cxxMethodDecl(hasName("reset"))),
+ hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)))
+ .bind(ResetCall),
+ this);
+}
+
+void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
+ // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
+ // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
+ // 'std::make_unique' or other function that creates smart_ptr.
+
+ SourceManager &SM = *Result.SourceManager;
+ const auto *Construct =
+ Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
+ const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
+ const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
+ const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
+
+ if (New->getNumPlacementArgs() != 0)
+ return;
+
+ if (Construct)
+ checkConstruct(SM, Construct, Type, New);
+ else if (Reset)
+ checkReset(SM, Reset, New);
+}
+
+void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
+ const CXXConstructExpr *Construct,
+ const QualType *Type,
+ const CXXNewExpr *New) {
+ SourceLocation ConstructCallStart = Construct->getExprLoc();
+
+ bool Invalid = false;
+ StringRef ExprStr = Lexer::getSourceText(
+ CharSourceRange::getCharRange(
+ ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
+ SM, getLangOpts(), &Invalid);
+ if (Invalid)
+ return;
+
+ auto Diag = diag(ConstructCallStart, "use %0 instead")
+ << MakeSmartPtrFunctionName;
+
+ // Find the location of the template's left angle.
+ size_t LAngle = ExprStr.find("<");
+ SourceLocation ConstructCallEnd;
+ if (LAngle == StringRef::npos) {
+ // If the template argument is missing (because it is part of the alias)
+ // we have to add it back.
+ ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
+ Diag << FixItHint::CreateInsertion(
+ ConstructCallEnd,
+ "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
+ } else {
+ ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
+ }
+
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
+ MakeSmartPtrFunctionName);
+
+ // If the smart_ptr is built with brace enclosed direct initialization, use
+ // parenthesis instead.
+ if (Construct->isListInitialization()) {
+ SourceRange BraceRange = Construct->getParenOrBraceRange();
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(
+ BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
+ "(");
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(BraceRange.getEnd(),
+ BraceRange.getEnd().getLocWithOffset(1)),
+ ")");
+ }
+
+ replaceNew(Diag, New, SM);
+ insertHeader(Diag, SM.getFileID(ConstructCallStart));
+}
+
+void MakeSmartPtrCheck::checkReset(SourceManager &SM,
+ const CXXMemberCallExpr *Reset,
+ const CXXNewExpr *New) {
+ const auto *Expr = cast<MemberExpr>(Reset->getCallee());
+ SourceLocation OperatorLoc = Expr->getOperatorLoc();
+ SourceLocation ResetCallStart = Reset->getExprLoc();
+ SourceLocation ExprStart = Expr->getLocStart();
+ SourceLocation ExprEnd =
+ Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
+
+ auto Diag = diag(ResetCallStart, "use %0 instead")
+ << MakeSmartPtrFunctionName;
+
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
+ (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
+ GetNewExprName(New, SM, getLangOpts()) + ">")
+ .str());
+
+ if (Expr->isArrow())
+ Diag << FixItHint::CreateInsertion(ExprStart, "*");
+
+ replaceNew(Diag, New, SM);
+ insertHeader(Diag, SM.getFileID(OperatorLoc));
+}
+
+void MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
+ const CXXNewExpr *New,
+ SourceManager& SM) {
+ SourceLocation NewStart = New->getSourceRange().getBegin();
+ SourceLocation NewEnd = New->getSourceRange().getEnd();
+
+ std::string ArraySizeExpr;
+ if (const auto* ArraySize = New->getArraySize()) {
+ ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
+ ArraySize->getSourceRange()),
+ SM, getLangOpts())
+ .str();
+ }
+
+ switch (New->getInitializationStyle()) {
+ case CXXNewExpr::NoInit: {
+ if (ArraySizeExpr.empty()) {
+ Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
+ } else {
+ // New array expression without written initializer:
+ // smart_ptr<Foo[]>(new Foo[5]);
+ Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
+ ArraySizeExpr);
+ }
+ break;
+ }
+ case CXXNewExpr::CallInit: {
+ if (ArraySizeExpr.empty()) {
+ SourceRange InitRange = New->getDirectInitRange();
+ Diag << FixItHint::CreateRemoval(
+ SourceRange(NewStart, InitRange.getBegin()));
+ Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
+ }
+ else {
+ // New array expression with default/value initialization:
+ // smart_ptr<Foo[]>(new int[5]());
+ // smart_ptr<Foo[]>(new Foo[5]());
+ Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
+ ArraySizeExpr);
+ }
+ break;
+ }
+ case CXXNewExpr::ListInit: {
+ // Range of the substring that we do not want to remove.
+ SourceRange InitRange;
+ if (const auto *NewConstruct = New->getConstructExpr()) {
+ // Direct initialization with initialization list.
+ // struct S { S(int x) {} };
+ // smart_ptr<S>(new S{5});
+ // The arguments in the initialization list are going to be forwarded to
+ // the constructor, so this has to be replaced with:
+ // struct S { S(int x) {} };
+ // std::make_smart_ptr<S>(5);
+ InitRange = SourceRange(
+ NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
+ NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
+ } else {
+ // Aggregate initialization.
+ // smart_ptr<Pair>(new Pair{first, second});
+ // Has to be replaced with:
+ // smart_ptr<Pair>(Pair{first, second});
+ InitRange = SourceRange(
+ New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
+ New->getInitializer()->getSourceRange().getEnd());
+ }
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
+ Diag << FixItHint::CreateRemoval(
+ SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
+ break;
+ }
+ }
+}
+
+void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
+ if (MakeSmartPtrFunctionHeader.empty()) {
+ return;
+ }
+ if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+ FD, MakeSmartPtrFunctionHeader,
+ /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
+ Diag << *IncludeFixit;
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MakeSmartPtrCheck.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_MODERNIZE_MAKE_SMART_PTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Base class for MakeSharedCheck and MakeUniqueCheck.
+class MakeSmartPtrCheck : public ClangTidyCheck {
+public:
+ MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
+ StringRef MakeSmartPtrFunctionName);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+ void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+protected:
+ using SmartPtrTypeMatcher = ast_matchers::internal::BindableMatcher<QualType>;
+
+ /// Returns matcher that match with different smart pointer types.
+ ///
+ /// Requires to bind pointer type (qualType) with PointerType string declared
+ /// in this class.
+ virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const = 0;
+
+ static const char PointerType[];
+ static const char ConstructorCall[];
+ static const char ResetCall[];
+ static const char NewExpression[];
+
+private:
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+ const std::string MakeSmartPtrFunctionHeader;
+ const std::string MakeSmartPtrFunctionName;
+
+ void checkConstruct(SourceManager &SM, const CXXConstructExpr *Construct,
+ const QualType *Type, const CXXNewExpr *New);
+ void checkReset(SourceManager &SM, const CXXMemberCallExpr *Member,
+ const CXXNewExpr *New);
+
+ void replaceNew(DiagnosticBuilder &Diag, const CXXNewExpr *New,
+ SourceManager &SM);
+ void insertHeader(DiagnosticBuilder &Diag, FileID FD);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
--- /dev/null
+//===--- MakeUniqueCheck.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 "MakeUniqueCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+MakeUniqueCheck::MakeUniqueCheck(StringRef Name,
+ clang::tidy::ClangTidyContext *Context)
+ : MakeSmartPtrCheck(Name, Context, "std::make_unique") {}
+
+MakeUniqueCheck::SmartPtrTypeMatcher
+MakeUniqueCheck::getSmartPointerTypeMatcher() const {
+ return qualType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("::std::unique_ptr"), templateArgumentCountIs(2),
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(qualType().bind(PointerType)))),
+ hasTemplateArgument(
+ 1,
+ templateArgument(refersToType(
+ qualType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("::std::default_delete"), templateArgumentCountIs(1),
+ hasTemplateArgument(
+ 0, templateArgument(refersToType(
+ qualType(equalsBoundNode(PointerType))))))))))))));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MakeUniqueCheck.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_MODERNIZE_MAKE_UNIQUE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
+
+#include "MakeSmartPtrCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace the pattern:
+/// \code
+/// std::unique_ptr<type>(new type(args...))
+/// \endcode
+///
+/// With the C++14 version:
+/// \code
+/// std::make_unique<type>(args...)
+/// \endcode
+class MakeUniqueCheck : public MakeSmartPtrCheck {
+public:
+ MakeUniqueCheck(StringRef Name, ClangTidyContext *Context);
+
+protected:
+ SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
--- /dev/null
+//===--- ModernizeTidyModule.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 "AvoidBindCheck.h"
+#include "DeprecatedHeadersCheck.h"
+#include "LoopConvertCheck.h"
+#include "MakeSharedCheck.h"
+#include "MakeUniqueCheck.h"
+#include "PassByValueCheck.h"
+#include "RawStringLiteralCheck.h"
+#include "RedundantVoidArgCheck.h"
+#include "ReplaceAutoPtrCheck.h"
+#include "ReplaceRandomShuffleCheck.h"
+#include "ReturnBracedInitListCheck.h"
+#include "ShrinkToFitCheck.h"
+#include "UnaryStaticAssertCheck.h"
+#include "UseAutoCheck.h"
+#include "UseBoolLiteralsCheck.h"
+#include "UseDefaultMemberInitCheck.h"
+#include "UseEmplaceCheck.h"
+#include "UseEqualsDefaultCheck.h"
+#include "UseEqualsDeleteCheck.h"
+#include "UseNoexceptCheck.h"
+#include "UseNullptrCheck.h"
+#include "UseOverrideCheck.h"
+#include "UseTransparentFunctorsCheck.h"
+#include "UseUsingCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class ModernizeModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AvoidBindCheck>("modernize-avoid-bind");
+ CheckFactories.registerCheck<DeprecatedHeadersCheck>(
+ "modernize-deprecated-headers");
+ CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
+ CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
+ CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
+ CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
+ CheckFactories.registerCheck<RawStringLiteralCheck>(
+ "modernize-raw-string-literal");
+ CheckFactories.registerCheck<RedundantVoidArgCheck>(
+ "modernize-redundant-void-arg");
+ CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
+ "modernize-replace-auto-ptr");
+ CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
+ "modernize-replace-random-shuffle");
+ CheckFactories.registerCheck<ReturnBracedInitListCheck>(
+ "modernize-return-braced-init-list");
+ CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
+ CheckFactories.registerCheck<UnaryStaticAssertCheck>(
+ "modernize-unary-static-assert");
+ CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
+ CheckFactories.registerCheck<UseBoolLiteralsCheck>(
+ "modernize-use-bool-literals");
+ CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
+ "modernize-use-default-member-init");
+ CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
+ CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
+ CheckFactories.registerCheck<UseEqualsDeleteCheck>(
+ "modernize-use-equals-delete");
+ CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
+ CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
+ CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+ CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
+ "modernize-use-transparent-functors");
+ CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
+ }
+
+ ClangTidyOptions getModuleOptions() override {
+ ClangTidyOptions Options;
+ auto &Opts = Options.CheckOptions;
+ // For types whose size in bytes is above this threshold, we prefer taking a
+ // const-reference than making a copy.
+ Opts["modernize-loop-convert.MaxCopySize"] = "16";
+
+ Opts["modernize-loop-convert.MinConfidence"] = "reasonable";
+ Opts["modernize-loop-convert.NamingStyle"] = "CamelCase";
+ Opts["modernize-pass-by-value.IncludeStyle"] = "llvm"; // Also: "google".
+ Opts["modernize-replace-auto-ptr.IncludeStyle"] = "llvm"; // Also: "google".
+
+ // Comma-separated list of macros that behave like NULL.
+ Opts["modernize-use-nullptr.NullMacros"] = "NULL";
+ return Options;
+ }
+};
+
+// Register the ModernizeTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<ModernizeModule> X("modernize-module",
+ "Add modernize checks.");
+
+} // namespace modernize
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the ModernizeModule.
+volatile int ModernizeModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- PassByValueCheck.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 "PassByValueCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Matches move-constructible classes.
+///
+/// Given
+/// \code
+/// // POD types are trivially move constructible.
+/// struct Foo { int a; };
+///
+/// struct Bar {
+/// Bar(Bar &&) = deleted;
+/// int a;
+/// };
+/// \endcode
+/// recordDecl(isMoveConstructible())
+/// matches "Foo".
+AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
+ for (const CXXConstructorDecl *Ctor : Node.ctors()) {
+ if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
+ return true;
+ }
+ return false;
+}
+
+static TypeMatcher constRefType() {
+ return lValueReferenceType(pointee(isConstQualified()));
+}
+
+static TypeMatcher nonConstValueType() {
+ return qualType(unless(anyOf(referenceType(), isConstQualified())));
+}
+
+/// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
+///
+/// Checks both in the init-list and the body of the constructor.
+static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
+ const ParmVarDecl *ParamDecl) {
+ /// \brief \c clang::RecursiveASTVisitor that checks that the given
+ /// \c ParmVarDecl is used exactly one time.
+ ///
+ /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
+ class ExactlyOneUsageVisitor
+ : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
+ friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
+
+ public:
+ ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
+ : ParamDecl(ParamDecl) {}
+
+ /// \brief Whether or not the parameter variable is referred only once in
+ /// the
+ /// given constructor.
+ bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
+ Count = 0;
+ TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
+ return Count == 1;
+ }
+
+ private:
+ /// \brief Counts the number of references to a variable.
+ ///
+ /// Stops the AST traversal if more than one usage is found.
+ bool VisitDeclRefExpr(DeclRefExpr *D) {
+ if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
+ if (To == ParamDecl) {
+ ++Count;
+ if (Count > 1) {
+ // No need to look further, used more than once.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ const ParmVarDecl *ParamDecl;
+ unsigned Count;
+ };
+
+ return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
+}
+
+/// \brief Find all references to \p ParamDecl across all of the
+/// redeclarations of \p Ctor.
+static SmallVector<const ParmVarDecl *, 2>
+collectParamDecls(const CXXConstructorDecl *Ctor,
+ const ParmVarDecl *ParamDecl) {
+ SmallVector<const ParmVarDecl *, 2> Results;
+ unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
+
+ for (const FunctionDecl *Redecl : Ctor->redecls())
+ Results.push_back(Redecl->getParamDecl(ParamIdx));
+ return Results;
+}
+
+PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))),
+ ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
+
+void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+ Options.store(Opts, "ValuesOnly", ValuesOnly);
+}
+
+void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ forEachConstructorInitializer(
+ cxxCtorInitializer(
+ unless(isBaseInitializer()),
+ // Clang builds a CXXConstructExpr only when it knows which
+ // constructor will be called. In dependent contexts a
+ // ParenListExpr is generated instead of a CXXConstructExpr,
+ // filtering out templates automatically for us.
+ withInitializer(cxxConstructExpr(
+ has(ignoringParenImpCasts(declRefExpr(to(
+ parmVarDecl(
+ hasType(qualType(
+ // Match only const-ref or a non-const value
+ // parameters. Rvalues and const-values
+ // shouldn't be modified.
+ ValuesOnly ? nonConstValueType()
+ : anyOf(constRefType(),
+ nonConstValueType()))))
+ .bind("Param"))))),
+ hasDeclaration(cxxConstructorDecl(
+ isCopyConstructor(), unless(isDeleted()),
+ hasDeclContext(
+ cxxRecordDecl(isMoveConstructible())))))))
+ .bind("Initializer")))
+ .bind("Ctor"),
+ this);
+}
+
+void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ // Only register the preprocessor callbacks for C++; the functionality
+ // currently does not provide any benefit to other languages, despite being
+ // benign.
+ if (getLangOpts().CPlusPlus) {
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+ }
+}
+
+void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
+ const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
+ const auto *Initializer =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
+ SourceManager &SM = *Result.SourceManager;
+
+ // If the parameter is used or anything other than the copy, do not apply
+ // the changes.
+ if (!paramReferredExactlyOnce(Ctor, ParamDecl))
+ return;
+
+ // 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().getNonReferenceType().isTriviallyCopyableType(
+ *Result.Context))
+ return;
+
+ auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move");
+
+ // Iterate over all declarations of the constructor.
+ for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
+ auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
+ auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
+
+ // Do not replace if it is already a value, skip.
+ if (RefTL.isNull())
+ continue;
+
+ TypeLoc ValueTL = RefTL.getPointeeLoc();
+ auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(),
+ ParamTL.getLocEnd());
+ std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
+ ValueTL.getSourceRange()),
+ SM, getLangOpts())
+ .str();
+ ValueStr += ' ';
+ Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
+ }
+
+ // Use std::move in the initialization list.
+ Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
+ << FixItHint::CreateInsertion(
+ Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
+
+ if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+ Result.SourceManager->getFileID(Initializer->getSourceLocation()),
+ "utility",
+ /*IsAngled=*/true)) {
+ Diag << *IncludeFixit;
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- PassByValueCheck.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_MODERNIZE_PASS_BY_VALUE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class PassByValueCheck : public ClangTidyCheck {
+public:
+ PassByValueCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+ const bool ValuesOnly;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
--- /dev/null
+//===--- RawStringLiteralCheck.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 "RawStringLiteralCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+bool containsEscapes(StringRef HayStack, StringRef Escapes) {
+ size_t BackSlash = HayStack.find('\\');
+ if (BackSlash == StringRef::npos)
+ return false;
+
+ while (BackSlash != StringRef::npos) {
+ if (Escapes.find(HayStack[BackSlash + 1]) == StringRef::npos)
+ return false;
+ BackSlash = HayStack.find('\\', BackSlash + 2);
+ }
+
+ return true;
+}
+
+bool isRawStringLiteral(StringRef Text) {
+ // Already a raw string literal if R comes before ".
+ const size_t QuotePos = Text.find('"');
+ assert(QuotePos != StringRef::npos);
+ return (QuotePos > 0) && (Text[QuotePos - 1] == 'R');
+}
+
+bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
+ const StringLiteral *Literal) {
+ // FIXME: Handle L"", u8"", u"" and U"" literals.
+ if (!Literal->isAscii())
+ return false;
+
+ StringRef Bytes = Literal->getBytes();
+ // Non-printing characters disqualify this literal:
+ // \007 = \a bell
+ // \010 = \b backspace
+ // \011 = \t horizontal tab
+ // \012 = \n new line
+ // \013 = \v vertical tab
+ // \014 = \f form feed
+ // \015 = \r carriage return
+ // \177 = delete
+ if (Bytes.find_first_of(StringRef("\000\001\002\003\004\005\006\a"
+ "\b\t\n\v\f\r\016\017"
+ "\020\021\022\023\024\025\026\027"
+ "\030\031\032\033\034\035\036\037"
+ "\177",
+ 33)) != StringRef::npos)
+ return false;
+
+ CharSourceRange CharRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(Literal->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+ StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
+ Result.Context->getLangOpts());
+ if (isRawStringLiteral(Text))
+ return false;
+
+ return containsEscapes(Text, R"('\"?x01)");
+}
+
+bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
+ return Bytes.find(Delimiter.empty()
+ ? std::string(R"lit()")lit")
+ : (")" + Delimiter + R"(")")) != StringRef::npos;
+}
+
+std::string asRawStringLiteral(const StringLiteral *Literal,
+ const std::string &DelimiterStem) {
+ const StringRef Bytes = Literal->getBytes();
+ std::string Delimiter;
+ for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
+ Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
+ }
+
+ if (Delimiter.empty())
+ return (R"(R"()" + Bytes + R"lit()")lit").str();
+
+ return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
+}
+
+} // namespace
+
+RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ DelimiterStem(Options.get("DelimiterStem", "lit")),
+ ReplaceShorterLiterals(Options.get("ReplaceShorterLiterals", false)) {}
+
+void RawStringLiteralCheck::storeOptions(ClangTidyOptions::OptionMap &Options) {
+ ClangTidyCheck::storeOptions(Options);
+ this->Options.store(Options, "ReplaceShorterLiterals",
+ ReplaceShorterLiterals);
+}
+
+void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
+ // Raw string literals require C++11 or later.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
+}
+
+void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+ if (Literal->getLocStart().isMacroID())
+ return;
+
+ if (containsEscapedCharacters(Result, Literal)) {
+ std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
+ if (ReplaceShorterLiterals ||
+ Replacement.length() <=
+ Lexer::MeasureTokenLength(Literal->getLocStart(),
+ *Result.SourceManager, getLangOpts()))
+ replaceWithRawStringLiteral(Result, Literal, Replacement);
+ }
+}
+
+void RawStringLiteralCheck::replaceWithRawStringLiteral(
+ const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
+ StringRef Replacement) {
+ CharSourceRange CharRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(Literal->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ diag(Literal->getLocStart(),
+ "escaped string literal can be written as a raw string literal")
+ << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RawStringLiteralCheck.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_MODERNIZE_RAW_STRING_LITERAL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check replaces string literals with escaped characters to
+/// raw string literals.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-raw-string-literal.html
+class RawStringLiteralCheck : public ClangTidyCheck {
+public:
+ RawStringLiteralCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void replaceWithRawStringLiteral(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const StringLiteral *Literal, StringRef Replacement);
+
+ std::string DelimiterStem;
+ const bool ReplaceShorterLiterals;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
--- /dev/null
+//===- RedundantVoidArgCheck.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 "RedundantVoidArgCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+// Determine if the given QualType is a nullary function or pointer to same.
+bool protoTypeHasNoParms(QualType QT) {
+ if (auto PT = QT->getAs<PointerType>()) {
+ QT = PT->getPointeeType();
+ }
+ if (auto *MPT = QT->getAs<MemberPointerType>()) {
+ QT = MPT->getPointeeType();
+ }
+ if (auto FP = QT->getAs<FunctionProtoType>()) {
+ return FP->getNumParams() == 0;
+ }
+ return false;
+}
+
+const char FunctionId[] = "function";
+const char TypedefId[] = "typedef";
+const char FieldId[] = "field";
+const char VarId[] = "var";
+const char NamedCastId[] = "named-cast";
+const char CStyleCastId[] = "c-style-cast";
+const char ExplicitCastId[] = "explicit-cast";
+const char LambdaId[] = "lambda";
+
+} // namespace
+
+void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
+ unless(isExternC()))
+ .bind(FunctionId),
+ this);
+ Finder->addMatcher(typedefNameDecl().bind(TypedefId), this);
+ auto ParenFunctionType = parenType(innerType(functionType()));
+ auto PointerToFunctionType = pointee(ParenFunctionType);
+ auto FunctionOrMemberPointer =
+ anyOf(hasType(pointerType(PointerToFunctionType)),
+ hasType(memberPointerType(PointerToFunctionType)));
+ Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
+ Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
+ auto CastDestinationIsFunction =
+ hasDestinationType(pointsTo(ParenFunctionType));
+ Finder->addMatcher(
+ cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
+ Finder->addMatcher(
+ cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
+ Finder->addMatcher(
+ cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
+ this);
+ Finder->addMatcher(
+ cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
+ Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
+}
+
+void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
+ const BoundNodes &Nodes = Result.Nodes;
+ if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
+ processFunctionDecl(Result, Function);
+ } else if (const auto *TypedefName =
+ Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
+ processTypedefNameDecl(Result, TypedefName);
+ } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
+ processFieldDecl(Result, Member);
+ } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
+ processVarDecl(Result, Var);
+ } else if (const auto *NamedCast =
+ Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
+ processNamedCastExpr(Result, NamedCast);
+ } else if (const auto *CStyleCast =
+ Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
+ processExplicitCastExpr(Result, CStyleCast);
+ } else if (const auto *ExplicitCast =
+ Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
+ processExplicitCastExpr(Result, ExplicitCast);
+ } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
+ processLambdaExpr(Result, Lambda);
+ }
+}
+
+void RedundantVoidArgCheck::processFunctionDecl(
+ const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
+ if (Function->isThisDeclarationADefinition()) {
+ const Stmt *Body = Function->getBody();
+ SourceLocation Start = Function->getLocStart();
+ SourceLocation End =
+ Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
+ removeVoidArgumentTokens(Result, SourceRange(Start, End),
+ "function definition");
+ } else {
+ removeVoidArgumentTokens(Result, Function->getSourceRange(),
+ "function declaration");
+ }
+}
+
+void RedundantVoidArgCheck::removeVoidArgumentTokens(
+ const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
+ StringRef GrammarLocation) {
+ CharSourceRange CharRange =
+ Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
+ *Result.SourceManager, getLangOpts());
+
+ std::string DeclText =
+ Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
+ .str();
+ Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
+ DeclText.data(), DeclText.data() + DeclText.size());
+ enum TokenState {
+ NothingYet,
+ SawLeftParen,
+ SawVoid,
+ };
+ TokenState State = NothingYet;
+ Token VoidToken;
+ Token ProtoToken;
+ std::string Diagnostic =
+ ("redundant void argument list in " + GrammarLocation).str();
+
+ while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
+ switch (State) {
+ case NothingYet:
+ if (ProtoToken.is(tok::TokenKind::l_paren)) {
+ State = SawLeftParen;
+ }
+ break;
+ case SawLeftParen:
+ if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
+ ProtoToken.getRawIdentifier() == "void") {
+ State = SawVoid;
+ VoidToken = ProtoToken;
+ } else {
+ State = NothingYet;
+ }
+ break;
+ case SawVoid:
+ State = NothingYet;
+ if (ProtoToken.is(tok::TokenKind::r_paren)) {
+ removeVoidToken(VoidToken, Diagnostic);
+ } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
+ State = SawLeftParen;
+ }
+ break;
+ }
+ }
+
+ if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
+ removeVoidToken(VoidToken, Diagnostic);
+ }
+}
+
+void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
+ StringRef Diagnostic) {
+ SourceLocation VoidLoc(VoidToken.getLocation());
+ auto VoidRange =
+ CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3));
+ diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange);
+}
+
+void RedundantVoidArgCheck::processTypedefNameDecl(
+ const MatchFinder::MatchResult &Result,
+ const TypedefNameDecl *TypedefName) {
+ if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
+ removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
+ isa<TypedefDecl>(TypedefName) ? "typedef"
+ : "type alias");
+ }
+}
+
+void RedundantVoidArgCheck::processFieldDecl(
+ const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
+ if (protoTypeHasNoParms(Member->getType())) {
+ removeVoidArgumentTokens(Result, Member->getSourceRange(),
+ "field declaration");
+ }
+}
+
+void RedundantVoidArgCheck::processVarDecl(
+ const MatchFinder::MatchResult &Result, const VarDecl *Var) {
+ if (protoTypeHasNoParms(Var->getType())) {
+ SourceLocation Begin = Var->getLocStart();
+ if (Var->hasInit()) {
+ SourceLocation InitStart =
+ Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
+ .getLocWithOffset(-1);
+ removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
+ "variable declaration with initializer");
+ } else {
+ removeVoidArgumentTokens(Result, Var->getSourceRange(),
+ "variable declaration");
+ }
+ }
+}
+
+void RedundantVoidArgCheck::processNamedCastExpr(
+ const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
+ if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
+ removeVoidArgumentTokens(
+ Result,
+ NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
+ "named cast");
+ }
+}
+
+void RedundantVoidArgCheck::processExplicitCastExpr(
+ const MatchFinder::MatchResult &Result,
+ const ExplicitCastExpr *ExplicitCast) {
+ if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
+ removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
+ "cast expression");
+ }
+}
+
+void RedundantVoidArgCheck::processLambdaExpr(
+ const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
+ if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
+ Lambda->hasExplicitParameters()) {
+ SourceLocation Begin =
+ Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
+ SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
+ removeVoidArgumentTokens(Result, SourceRange(Begin, End),
+ "lambda expression");
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantVoidArgCheck.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_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
+
+#include "../ClangTidy.h"
+#include "clang/Lex/Token.h"
+
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Find and remove redundant void argument lists.
+///
+/// Examples:
+/// `int f(void);` becomes `int f();`
+/// `int (*f(void))(void);` becomes `int (*f())();`
+/// `typedef int (*f_t(void))(void);` becomes `typedef int (*f_t())();`
+/// `void (C::*p)(void);` becomes `void (C::*p)();`
+/// `C::C(void) {}` becomes `C::C() {}`
+/// `C::~C(void) {}` becomes `C::~C() {}`
+///
+class RedundantVoidArgCheck : public ClangTidyCheck {
+public:
+ RedundantVoidArgCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void processFunctionDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+ const FunctionDecl *Function);
+
+ void
+ processTypedefNameDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+ const TypedefNameDecl *Typedef);
+
+ void processFieldDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+ const FieldDecl *Member);
+
+ void processVarDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+ const VarDecl *Var);
+
+ void
+ processNamedCastExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXNamedCastExpr *NamedCast);
+
+ void
+ processExplicitCastExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+ const ExplicitCastExpr *ExplicitCast);
+
+ void processLambdaExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+ const LambdaExpr *Lambda);
+
+ void
+ removeVoidArgumentTokens(const ast_matchers::MatchFinder::MatchResult &Result,
+ SourceRange Range, StringRef GrammarLocation);
+
+ void removeVoidToken(Token VoidToken, StringRef Diagnostic);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
--- /dev/null
+//===--- ReplaceAutoPtrCheck.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 "ReplaceAutoPtrCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char AutoPtrTokenId[] = "AutoPrTokenId";
+static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId";
+
+/// \brief Matches expressions that are lvalues.
+///
+/// In the following example, a[0] matches expr(isLValue()):
+/// \code
+/// std::string a[2];
+/// std::string b;
+/// b = a[0];
+/// b = "this string won't match";
+/// \endcode
+AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }
+
+/// Matches declarations whose declaration context is the C++ standard library
+/// namespace std.
+///
+/// Note that inline namespaces are silently ignored during the lookup since
+/// both libstdc++ and libc++ are known to use them for versioning purposes.
+///
+/// Given:
+/// \code
+/// namespace ns {
+/// struct my_type {};
+/// using namespace std;
+/// }
+///
+/// using std::vector;
+/// using ns:my_type;
+/// using ns::list;
+/// \code
+///
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
+/// matches "using std::vector" and "using ns::list".
+AST_MATCHER(Decl, isFromStdNamespace) {
+ const DeclContext *D = Node.getDeclContext();
+
+ while (D->isInlineNamespace())
+ D = D->getParent();
+
+ if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
+ return false;
+
+ const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
+
+ return (Info && Info->isStr("std"));
+}
+
+ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace());
+ auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
+
+ // std::auto_ptr<int> a;
+ // ^~~~~~~~~~~~~
+ //
+ // typedef std::auto_ptr<int> int_ptr_t;
+ // ^~~~~~~~~~~~~
+ //
+ // std::auto_ptr<int> fn(std::auto_ptr<int>);
+ // ^~~~~~~~~~~~~ ^~~~~~~~~~~~~
+ Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
+ // Skip elaboratedType() as the named
+ // type will match soon thereafter.
+ unless(elaboratedType()))))
+ .bind(AutoPtrTokenId),
+ this);
+
+ // using std::auto_ptr;
+ // ^~~~~~~~~~~~~~~~~~~
+ Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(allOf(
+ hasName("auto_ptr"), isFromStdNamespace()))))
+ .bind(AutoPtrTokenId),
+ this);
+
+ // Find ownership transfers via copy construction and assignment.
+ // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped
+ // into std::move().
+ // std::auto_ptr<int> i, j;
+ // i = j;
+ // ~~~~^
+ auto MovableArgumentMatcher =
+ expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);
+
+ Finder->addMatcher(
+ cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+ callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
+ hasArgument(1, MovableArgumentMatcher)),
+ this);
+ Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
+ hasArgument(0, MovableArgumentMatcher)),
+ this);
+}
+
+void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ // Only register the preprocessor callbacks for C++; the functionality
+ // currently does not provide any benefit to other languages, despite being
+ // benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
+ SourceManager &SM = *Result.SourceManager;
+ if (const auto *E =
+ Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
+ CharSourceRange Range = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
+
+ if (Range.isInvalid())
+ return;
+
+ auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
+ << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
+ << FixItHint::CreateInsertion(Range.getEnd(), ")");
+
+ if (auto Fix =
+ Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
+ /*IsAngled=*/true))
+ Diag << *Fix;
+
+ return;
+ }
+
+ SourceLocation AutoPtrLoc;
+ if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
+ // std::auto_ptr<int> i;
+ // ^
+ if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
+ AutoPtrLoc = Loc.getTemplateNameLoc();
+ } else if (const auto *D =
+ Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
+ // using std::auto_ptr;
+ // ^
+ AutoPtrLoc = D->getNameInfo().getBeginLoc();
+ } else {
+ llvm_unreachable("Bad Callback. No node provided.");
+ }
+
+ if (AutoPtrLoc.isMacroID())
+ AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);
+
+ // Ensure that only the 'auto_ptr' token is replaced and not the template
+ // aliases.
+ if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
+ "auto_ptr")
+ return;
+
+ SourceLocation EndLoc =
+ AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
+ diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
+ << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
+ "unique_ptr");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ReplaceAutoPtrCheck.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_MODERNIZE_REPLACE_AUTO_PTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_AUTO_PTR_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Transforms the deprecated `std::auto_ptr` into the C++11 `std::unique_ptr`.
+///
+/// Note that both the `std::auto_ptr` type and the transfer of ownership are
+/// transformed. `std::auto_ptr` provides two ways to transfer the ownership,
+/// the copy-constructor and the assignment operator. Unlike most classes these
+/// operations do not 'copy' the resource but they 'steal' it.
+/// `std::unique_ptr` uses move semantics instead, which makes the intent of
+/// transferring the resource explicit. This difference between the two smart
+/// pointers requeres to wrap the copy-ctor and assign-operator with
+/// `std::move()`.
+///
+/// For example, given:
+///
+/// \code
+/// std::auto_ptr<int> i, j;
+/// i = j;
+/// \endcode
+///
+/// This code is transformed to:
+///
+/// \code
+/// std::unique_ptr<in> i, j;
+/// i = std::move(j);
+/// \endcode
+class ReplaceAutoPtrCheck : public ClangTidyCheck {
+public:
+ ReplaceAutoPtrCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_AUTO_PTR_H
--- /dev/null
+//===--- ReplaceRandomShuffleCheck.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 "ReplaceRandomShuffleCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+ReplaceRandomShuffleCheck::ReplaceRandomShuffleCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ const auto Begin = hasArgument(0, expr());
+ const auto End = hasArgument(1, expr());
+ const auto RandomFunc = hasArgument(2, expr().bind("randomFunc"));
+ Finder->addMatcher(
+ callExpr(anyOf(allOf(Begin, End, argumentCountIs(2)),
+ allOf(Begin, End, RandomFunc, argumentCountIs(3))),
+ hasDeclaration(functionDecl(hasName("::std::random_shuffle"))),
+ has(implicitCastExpr(has(declRefExpr().bind("name")))))
+ .bind("match"),
+ this);
+}
+
+void ReplaceRandomShuffleCheck::registerPPCallbacks(
+ CompilerInstance &Compiler) {
+ IncludeInserter = llvm::make_unique<utils::IncludeInserter>(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
+ Compiler.getPreprocessor().addPPCallbacks(
+ IncludeInserter->CreatePPCallbacks());
+}
+
+void ReplaceRandomShuffleCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<DeclRefExpr>("name");
+ const auto *MatchedArgumentThree = Result.Nodes.getNodeAs<Expr>("randomFunc");
+ const auto *MatchedCallExpr = Result.Nodes.getNodeAs<CallExpr>("match");
+
+ if (MatchedCallExpr->getLocStart().isMacroID())
+ return;
+
+ auto Diag = [&] {
+ if (MatchedCallExpr->getNumArgs() == 3) {
+ auto DiagL =
+ diag(MatchedCallExpr->getLocStart(),
+ "'std::random_shuffle' has been removed in C++17; use "
+ "'std::shuffle' and an alternative random mechanism instead");
+ DiagL << FixItHint::CreateReplacement(
+ MatchedArgumentThree->getSourceRange(),
+ "std::mt19937(std::random_device()())");
+ return DiagL;
+ } else {
+ auto DiagL = diag(MatchedCallExpr->getLocStart(),
+ "'std::random_shuffle' has been removed in C++17; use "
+ "'std::shuffle' instead");
+ DiagL << FixItHint::CreateInsertion(
+ MatchedCallExpr->getRParenLoc(),
+ ", std::mt19937(std::random_device()())");
+ return DiagL;
+ }
+ }();
+
+ std::string NewName = "shuffle";
+ StringRef ContainerText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ if (ContainerText.startswith("std::"))
+ NewName = "std::" + NewName;
+
+ Diag << FixItHint::CreateRemoval(MatchedDecl->getSourceRange());
+ Diag << FixItHint::CreateInsertion(MatchedDecl->getLocStart(), NewName);
+
+ if (Optional<FixItHint> IncludeFixit =
+ IncludeInserter->CreateIncludeInsertion(
+ Result.Context->getSourceManager().getFileID(
+ MatchedCallExpr->getLocStart()),
+ "random", /*IsAngled=*/true))
+ Diag << IncludeFixit.getValue();
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ReplaceRandomShuffleCheck.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_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// std::random_shuffle will be removed as of C++17. This check will find and
+/// replace all occurrences of std::random_shuffle with std::shuffle.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-replace-random-shuffle.html
+class ReplaceRandomShuffleCheck : public ClangTidyCheck {
+public:
+ ReplaceRandomShuffleCheck(StringRef Name, ClangTidyContext *Context);
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ std::unique_ptr<utils::IncludeInserter> IncludeInserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
--- /dev/null
+//===--- ReturnBracedInitListCheck.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 "ReturnBracedInitListCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++.
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // Skip list initialization and constructors with an initializer list.
+ auto ConstructExpr =
+ cxxConstructExpr(
+ unless(anyOf(hasDeclaration(cxxConstructorDecl(isExplicit())),
+ isListInitialization(), hasDescendant(initListExpr()),
+ isInTemplateInstantiation())))
+ .bind("ctor");
+
+ auto CtorAsArgument = materializeTemporaryExpr(anyOf(
+ has(ConstructExpr), has(cxxFunctionalCastExpr(has(ConstructExpr)))));
+
+ Finder->addMatcher(
+ functionDecl(isDefinition(), // Declarations don't have return statements.
+ returns(unless(anyOf(builtinType(), autoType()))),
+ hasDescendant(returnStmt(hasReturnValue(
+ has(cxxConstructExpr(has(CtorAsArgument)))))))
+ .bind("fn"),
+ this);
+}
+
+void ReturnBracedInitListCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedFunctionDecl = Result.Nodes.getNodeAs<FunctionDecl>("fn");
+ const auto *MatchedConstructExpr =
+ Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+
+ // Don't make replacements in macro.
+ SourceLocation Loc = MatchedConstructExpr->getExprLoc();
+ if (Loc.isMacroID())
+ return;
+
+ // Make sure that the return type matches the constructed type.
+ const QualType ReturnType =
+ MatchedFunctionDecl->getReturnType().getCanonicalType();
+ const QualType ConstructType =
+ MatchedConstructExpr->getType().getCanonicalType();
+ if (ReturnType != ConstructType)
+ return;
+
+ auto Diag = diag(Loc, "avoid repeating the return type from the "
+ "declaration; use a braced initializer list instead");
+
+ const SourceRange CallParensRange =
+ MatchedConstructExpr->getParenOrBraceRange();
+
+ // Make sure there is an explicit constructor call.
+ if (CallParensRange.isInvalid())
+ return;
+
+ // Make sure that the ctor arguments match the declaration.
+ for (unsigned I = 0, NumParams = MatchedConstructExpr->getNumArgs();
+ I < NumParams; ++I) {
+ if (const auto *VD = dyn_cast<VarDecl>(
+ MatchedConstructExpr->getConstructor()->getParamDecl(I))) {
+ if (MatchedConstructExpr->getArg(I)->getType().getCanonicalType() !=
+ VD->getType().getCanonicalType())
+ return;
+ }
+ }
+
+ // Range for constructor name and opening brace.
+ CharSourceRange CtorCallSourceRange = CharSourceRange::getTokenRange(
+ Loc, CallParensRange.getBegin().getLocWithOffset(-1));
+
+ Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
+ << FixItHint::CreateReplacement(CallParensRange.getBegin(), "{")
+ << FixItHint::CreateReplacement(CallParensRange.getEnd(), "}");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ReturnBracedInitListCheck.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_MODERNIZE_RETURN_BRACED_INIT_LIST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Use a braced init list for return statements rather than unnecessary
+/// repeating the return type name.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html
+class ReturnBracedInitListCheck : public ClangTidyCheck {
+public:
+ ReturnBracedInitListCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H
--- /dev/null
+//===--- ShrinkToFitCheck.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 "ShrinkToFitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // Swap as a function need not to be considered, because rvalue can not
+ // be bound to a non-const reference.
+ const auto ShrinkableAsMember =
+ memberExpr(member(valueDecl().bind("ContainerDecl")));
+ const auto ShrinkableAsDecl =
+ declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl")));
+ const auto CopyCtorCall = cxxConstructExpr(hasArgument(
+ 0, anyOf(ShrinkableAsMember, ShrinkableAsDecl,
+ unaryOperator(has(ignoringParenImpCasts(ShrinkableAsMember))),
+ unaryOperator(has(ignoringParenImpCasts(ShrinkableAsDecl))))));
+ const auto SwapParam =
+ expr(anyOf(memberExpr(member(equalsBoundNode("ContainerDecl"))),
+ declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))),
+ unaryOperator(has(ignoringParenImpCasts(
+ memberExpr(member(equalsBoundNode("ContainerDecl")))))),
+ unaryOperator(has(ignoringParenImpCasts(declRefExpr(
+ hasDeclaration(equalsBoundNode("ContainerDecl"))))))));
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ on(hasType(namedDecl(
+ hasAnyName("std::basic_string", "std::deque", "std::vector")))),
+ callee(cxxMethodDecl(hasName("swap"))),
+ has(ignoringParenImpCasts(memberExpr(hasDescendant(CopyCtorCall)))),
+ hasArgument(0, SwapParam.bind("ContainerToShrink")),
+ unless(isInTemplateInstantiation()))
+ .bind("CopyAndSwapTrick"),
+ this);
+}
+
+void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemberCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("CopyAndSwapTrick");
+ const auto *Container = Result.Nodes.getNodeAs<Expr>("ContainerToShrink");
+ FixItHint Hint;
+
+ if (!MemberCall->getLocStart().isMacroID()) {
+ const LangOptions &Opts = getLangOpts();
+ std::string ReplacementText;
+ if (const auto *UnaryOp = llvm::dyn_cast<UnaryOperator>(Container)) {
+ ReplacementText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ UnaryOp->getSubExpr()->getSourceRange()),
+ *Result.SourceManager, Opts);
+ ReplacementText += "->shrink_to_fit()";
+ } else {
+ ReplacementText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Container->getSourceRange()),
+ *Result.SourceManager, Opts);
+ ReplacementText += ".shrink_to_fit()";
+ }
+
+ Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+ ReplacementText);
+ }
+
+ diag(MemberCall->getLocStart(), "the shrink_to_fit method should be used "
+ "to reduce the capacity of a shrinkable "
+ "container")
+ << Hint;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ShrinkToFitCheck.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_MODERNIZE_SHRINKTOFITCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_SHRINKTOFITCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace copy and swap tricks on shrinkable containers with the
+/// `shrink_to_fit()` method call.
+///
+/// The `shrink_to_fit()` method is more readable and more effective than
+/// the copy and swap trick to reduce the capacity of a shrinkable container.
+/// Note that, the `shrink_to_fit()` method is only available in C++11 and up.
+class ShrinkToFitCheck : public ClangTidyCheck {
+public:
+ ShrinkToFitCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_SHRINKTOFITCHECK_H
--- /dev/null
+//===--- UnaryStaticAssertCheck.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 "UnaryStaticAssertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void UnaryStaticAssertCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus1z)
+ return;
+
+ Finder->addMatcher(staticAssertDecl().bind("static_assert"), this);
+}
+
+void UnaryStaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<StaticAssertDecl>("static_assert");
+ const StringLiteral *AssertMessage = MatchedDecl->getMessage();
+
+ SourceLocation Loc = MatchedDecl->getLocation();
+
+ if (!AssertMessage || AssertMessage->getLength() ||
+ AssertMessage->getLocStart().isMacroID() || Loc.isMacroID())
+ return;
+
+ diag(Loc,
+ "use unary 'static_assert' when the string literal is an empty string")
+ << FixItHint::CreateRemoval(AssertMessage->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnaryStaticAssertCheck.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_MODERNIZE_UNARY_STATIC_ASSERT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_UNARY_STATIC_ASSERT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replaces a static_assert declaration with an empty message
+/// with the unary version.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-unary-static-assert.html
+class UnaryStaticAssertCheck : public ClangTidyCheck {
+public:
+ UnaryStaticAssertCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_UNARY_STATIC_ASSERT_H
--- /dev/null
+//===--- UseAutoCheck.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 "UseAutoCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+const char IteratorDeclStmtId[] = "iterator_decl";
+const char DeclWithNewId[] = "decl_new";
+const char DeclWithCastId[] = "decl_cast";
+const char DeclWithTemplateCastId[] = "decl_template";
+
+/// \brief Matches variable declarations that have explicit initializers that
+/// are not initializer lists.
+///
+/// Given
+/// \code
+/// iterator I = Container.begin();
+/// MyType A(42);
+/// MyType B{2};
+/// MyType C;
+/// \endcode
+///
+/// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
+/// or \c C.
+AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
+ const Expr *Init = Node.getAnyInitializer();
+ if (!Init)
+ return false;
+
+ Init = Init->IgnoreImplicit();
+
+ // The following test is based on DeclPrinter::VisitVarDecl() to find if an
+ // initializer is implicit or not.
+ if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
+ return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
+ !Construct->getArg(0)->isDefaultArgument();
+ }
+ return Node.getInitStyle() != VarDecl::ListInit;
+}
+
+/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
+/// SugarMatcher.
+///
+/// Given
+/// \code
+/// class C {};
+/// typedef C my_type;
+/// typedef my_type my_other_type;
+/// \endcode
+///
+/// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
+/// matches \c my_type and \c my_other_type.
+AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
+ QualType QT = Node;
+ while (true) {
+ if (SugarMatcher.matches(QT, Finder, Builder))
+ return true;
+
+ QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
+ if (NewQT == QT)
+ return false;
+ QT = NewQT;
+ }
+}
+
+/// \brief Matches named declarations that have one of the standard iterator
+/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
+///
+/// Given
+/// \code
+/// iterator I;
+/// const_iterator CI;
+/// \endcode
+///
+/// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
+AST_MATCHER(NamedDecl, hasStdIteratorName) {
+ static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
+ "const_iterator",
+ "const_reverse_iterator"};
+
+ for (const char *Name : IteratorNames) {
+ if (hasName(Name).matches(Node, Finder, Builder))
+ return true;
+ }
+ return false;
+}
+
+/// \brief Matches named declarations that have one of the standard container
+/// names.
+///
+/// Given
+/// \code
+/// class vector {};
+/// class forward_list {};
+/// class my_ver{};
+/// \endcode
+///
+/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
+/// but not \c my_vec.
+AST_MATCHER(NamedDecl, hasStdContainerName) {
+ static const char *const ContainerNames[] = {
+ "array", "deque",
+ "forward_list", "list",
+ "vector",
+
+ "map", "multimap",
+ "set", "multiset",
+
+ "unordered_map", "unordered_multimap",
+ "unordered_set", "unordered_multiset",
+
+ "queue", "priority_queue",
+ "stack"};
+
+ for (const char *Name : ContainerNames) {
+ if (hasName(Name).matches(Node, Finder, Builder))
+ return true;
+ }
+ return false;
+}
+
+/// Matches declarations whose declaration context is the C++ standard library
+/// namespace std.
+///
+/// Note that inline namespaces are silently ignored during the lookup since
+/// both libstdc++ and libc++ are known to use them for versioning purposes.
+///
+/// Given:
+/// \code
+/// namespace ns {
+/// struct my_type {};
+/// using namespace std;
+/// }
+///
+/// using std::vector;
+/// using ns:my_type;
+/// using ns::list;
+/// \code
+///
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
+/// matches "using std::vector" and "using ns::list".
+AST_MATCHER(Decl, isFromStdNamespace) {
+ const DeclContext *D = Node.getDeclContext();
+
+ while (D->isInlineNamespace())
+ D = D->getParent();
+
+ if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
+ return false;
+
+ const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
+
+ return (Info && Info->isStr("std"));
+}
+
+/// Matches declaration reference or member expressions with explicit template
+/// arguments.
+AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
+ MemberExpr)) {
+ return Node.hasExplicitTemplateArgs();
+}
+
+/// \brief Returns a DeclarationMatcher that matches standard iterators nested
+/// inside records with a standard container name.
+DeclarationMatcher standardIterator() {
+ return allOf(
+ namedDecl(hasStdIteratorName()),
+ hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
+}
+
+/// \brief Returns a TypeMatcher that matches typedefs for standard iterators
+/// inside records with a standard container name.
+TypeMatcher typedefIterator() {
+ return typedefType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches records named for standard
+/// iterators nested inside records named for standard containers.
+TypeMatcher nestedIterator() {
+ return recordType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches types declared with using
+/// declarations and which name standard iterators for standard containers.
+TypeMatcher iteratorFromUsingDeclaration() {
+ auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
+ // Types resulting from using declarations are represented by elaboratedType.
+ return elaboratedType(allOf(
+ // Unwrap the nested name specifier to test for one of the standard
+ // containers.
+ hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
+ namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
+ // the named type is what comes after the final '::' in the type. It
+ // should name one of the standard iterator names.
+ namesType(
+ anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
+}
+
+/// \brief This matcher returns declaration statements that contain variable
+/// declarations with written non-list initializer for standard iterators.
+StatementMatcher makeIteratorDeclMatcher() {
+ return declStmt(unless(has(
+ varDecl(anyOf(unless(hasWrittenNonListInitializer()),
+ unless(hasType(isSugarFor(anyOf(
+ typedefIterator(), nestedIterator(),
+ iteratorFromUsingDeclaration())))))))))
+ .bind(IteratorDeclStmtId);
+}
+
+StatementMatcher makeDeclWithNewMatcher() {
+ return declStmt(
+ unless(has(varDecl(anyOf(
+ unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
+ // FIXME: TypeLoc information is not reliable where CV
+ // qualifiers are concerned so these types can't be
+ // handled for now.
+ hasType(pointerType(
+ pointee(hasCanonicalType(hasLocalQualifiers())))),
+
+ // FIXME: Handle function pointers. For now we ignore them
+ // because the replacement replaces the entire type
+ // specifier source range which includes the identifier.
+ hasType(pointsTo(
+ pointsTo(parenType(innerType(functionType()))))))))))
+ .bind(DeclWithNewId);
+}
+
+StatementMatcher makeDeclWithCastMatcher() {
+ return declStmt(
+ unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
+ .bind(DeclWithCastId);
+}
+
+StatementMatcher makeDeclWithTemplateCastMatcher() {
+ auto ST =
+ substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg")));
+
+ auto ExplicitCall =
+ anyOf(has(memberExpr(hasExplicitTemplateArgs())),
+ has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
+
+ auto TemplateArg =
+ hasTemplateArgument(0, refersToType(qualType().bind("arg")));
+
+ auto TemplateCall = callExpr(
+ ExplicitCall,
+ callee(functionDecl(TemplateArg,
+ returns(anyOf(ST, pointsTo(ST), references(ST))))));
+
+ return declStmt(unless(has(varDecl(
+ unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
+ .bind(DeclWithTemplateCastId);
+}
+
+StatementMatcher makeCombinedMatcher() {
+ return declStmt(
+ // At least one varDecl should be a child of the declStmt to ensure
+ // it's a declaration list and avoid matching other declarations,
+ // e.g. using directives.
+ has(varDecl(unless(isImplicit()))),
+ // Skip declarations that are already using auto.
+ unless(has(varDecl(anyOf(hasType(autoType()),
+ hasType(qualType(hasDescendant(autoType()))))))),
+ anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
+ makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
+}
+
+} // namespace
+
+UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ RemoveStars(Options.get("RemoveStars", 0)) {}
+
+void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
+}
+
+void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (getLangOpts().CPlusPlus) {
+ Finder->addMatcher(makeCombinedMatcher(), this);
+ }
+}
+
+void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
+ for (const auto *Dec : D->decls()) {
+ const auto *V = cast<VarDecl>(Dec);
+ const Expr *ExprInit = V->getInit();
+
+ // Skip expressions with cleanups from the intializer expression.
+ if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
+ ExprInit = E->getSubExpr();
+
+ const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
+ if (!Construct)
+ continue;
+
+ // Ensure that the constructor receives a single argument.
+ if (Construct->getNumArgs() != 1)
+ return;
+
+ // Drill down to the as-written initializer.
+ const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
+ if (E != E->IgnoreConversionOperator()) {
+ // We hit a conversion operator. Early-out now as they imply an implicit
+ // conversion from a different type. Could also mean an explicit
+ // conversion from the same type but that's pretty rare.
+ return;
+ }
+
+ if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
+ // If we ran into an implicit conversion contructor, can't convert.
+ //
+ // FIXME: The following only checks if the constructor can be used
+ // implicitly, not if it actually was. Cases where the converting
+ // constructor was used explicitly won't get converted.
+ if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
+ return;
+ }
+ if (!Context->hasSameType(V->getType(), E->getType()))
+ return;
+ }
+
+ // Get the type location using the first declaration.
+ const auto *V = cast<VarDecl>(*D->decl_begin());
+
+ // WARNING: TypeLoc::getSourceRange() will include the identifier for things
+ // like function pointers. Not a concern since this action only works with
+ // iterators but something to keep in mind in the future.
+
+ SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
+ diag(Range.getBegin(), "use auto when declaring iterators")
+ << FixItHint::CreateReplacement(Range, "auto");
+}
+
+void UseAutoCheck::replaceExpr(
+ const DeclStmt *D, ASTContext *Context,
+ llvm::function_ref<QualType(const Expr *)> GetType, StringRef Message) {
+ const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
+ // Ensure that there is at least one VarDecl within the DeclStmt.
+ if (!FirstDecl)
+ return;
+
+ const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
+
+ std::vector<FixItHint> StarRemovals;
+ for (const auto *Dec : D->decls()) {
+ const auto *V = cast<VarDecl>(Dec);
+ // Ensure that every DeclStmt child is a VarDecl.
+ if (!V)
+ return;
+
+ const auto *Expr = V->getInit()->IgnoreParenImpCasts();
+ // Ensure that every VarDecl has an initializer.
+ if (!Expr)
+ return;
+
+ // If VarDecl and Initializer have mismatching unqualified types.
+ if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
+ return;
+
+ // All subsequent variables in this declaration should have the same
+ // canonical type. For example, we don't want to use `auto` in
+ // `T *p = new T, **pp = new T*;`.
+ if (FirstDeclType != V->getType().getCanonicalType())
+ return;
+
+ if (RemoveStars) {
+ // Remove explicitly written '*' from declarations where there's more than
+ // one declaration in the declaration list.
+ if (Dec == *D->decl_begin())
+ continue;
+
+ auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
+ while (!Q.isNull()) {
+ StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
+ Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
+ }
+ }
+ }
+
+ // FIXME: There is, however, one case we can address: when the VarDecl pointee
+ // is the same as the initializer, just more CV-qualified. However, TypeLoc
+ // information is not reliable where CV qualifiers are concerned so we can't
+ // do anything about this case for now.
+ TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
+ if (!RemoveStars) {
+ while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
+ Loc.getTypeLocClass() == TypeLoc::Qualified)
+ Loc = Loc.getNextTypeLoc();
+ }
+ while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
+ Loc.getTypeLocClass() == TypeLoc::RValueReference ||
+ Loc.getTypeLocClass() == TypeLoc::Qualified) {
+ Loc = Loc.getNextTypeLoc();
+ }
+ SourceRange Range(Loc.getSourceRange());
+ auto Diag = diag(Range.getBegin(), Message);
+
+ // Space after 'auto' to handle cases where the '*' in the pointer type is
+ // next to the identifier. This avoids changing 'int *p' into 'autop'.
+ // FIXME: This doesn't work for function pointers because the variable name
+ // is inside the type.
+ Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
+ << StarRemovals;
+}
+
+void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
+ replaceIterators(Decl, Result.Context);
+ } else if (const auto *Decl =
+ Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
+ replaceExpr(Decl, Result.Context,
+ [](const Expr *Expr) { return Expr->getType(); },
+ "use auto when initializing with new to avoid "
+ "duplicating the type name");
+ } else if (const auto *Decl =
+ Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
+ replaceExpr(
+ Decl, Result.Context,
+ [](const Expr *Expr) {
+ return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
+ },
+ "use auto when initializing with a cast to avoid duplicating the type "
+ "name");
+ } else if (const auto *Decl =
+ Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
+ replaceExpr(
+ Decl, Result.Context,
+ [](const Expr *Expr) {
+ return cast<CallExpr>(Expr->IgnoreImplicit())
+ ->getDirectCallee()
+ ->getReturnType();
+ },
+ "use auto when initializing with a template cast to avoid duplicating "
+ "the type name");
+ } else {
+ llvm_unreachable("Bad Callback. No node provided.");
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseAutoCheck.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_MODERNIZE_USE_AUTO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class UseAutoCheck : public ClangTidyCheck {
+public:
+ UseAutoCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void replaceIterators(const DeclStmt *D, ASTContext *Context);
+ void replaceExpr(const DeclStmt *D, ASTContext *Context,
+ llvm::function_ref<QualType(const Expr *)> GetType,
+ StringRef Message);
+
+ const bool RemoveStars;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H
--- /dev/null
+//===--- UseBoolLiteralsCheck.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 "UseBoolLiteralsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseBoolLiteralsCheck::UseBoolLiteralsCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void UseBoolLiteralsCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ implicitCastExpr(
+ has(ignoringParenImpCasts(integerLiteral().bind("literal"))),
+ hasImplicitDestinationType(qualType(booleanType())),
+ 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) {
+ const auto *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("literal");
+ const auto *Cast = Result.Nodes.getNodeAs<Expr>("cast");
+ bool LiteralBooleanValue = Literal->getValue().getBoolValue();
+
+ if (Literal->isInstantiationDependent())
+ return;
+
+ const Expr *Expression = Cast ? Cast : Literal;
+
+ bool InMacro = Expression->getLocStart().isMacroID();
+
+ if (InMacro && IgnoreMacros)
+ return;
+
+ auto Diag =
+ diag(Expression->getExprLoc(),
+ "converting integer literal to bool, use bool literal instead");
+
+ if (!InMacro)
+ Diag << FixItHint::CreateReplacement(
+ Expression->getSourceRange(), LiteralBooleanValue ? "true" : "false");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseBoolLiteralsCheck.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_MODERNIZE_USE_BOOL_LITERALS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_BOOL_LITERALS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Finds integer literals which are cast to bool.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-bool-literals.html
+class UseBoolLiteralsCheck : public ClangTidyCheck {
+public:
+ UseBoolLiteralsCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const bool IgnoreMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_BOOL_LITERALS_H
--- /dev/null
+//===--- UseDefaultMemberInitCheck.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 "UseDefaultMemberInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static StringRef getValueOfValueInit(const QualType InitType) {
+ switch (InitType->getScalarTypeKind()) {
+ case Type::STK_CPointer:
+ case Type::STK_BlockPointer:
+ case Type::STK_ObjCObjectPointer:
+ case Type::STK_MemberPointer:
+ return "nullptr";
+
+ case Type::STK_Bool:
+ return "false";
+
+ case Type::STK_Integral:
+ switch (InitType->getAs<BuiltinType>()->getKind()) {
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ case BuiltinType::Char_S:
+ case BuiltinType::SChar:
+ return "'\\0'";
+ case BuiltinType::WChar_U:
+ case BuiltinType::WChar_S:
+ return "L'\\0'";
+ case BuiltinType::Char16:
+ return "u'\\0'";
+ case BuiltinType::Char32:
+ return "U'\\0'";
+ default:
+ return "0";
+ }
+
+ case Type::STK_Floating:
+ switch (InitType->getAs<BuiltinType>()->getKind()) {
+ case BuiltinType::Half:
+ case BuiltinType::Float:
+ return "0.0f";
+ default:
+ return "0.0";
+ }
+
+ case Type::STK_FloatingComplex:
+ case Type::STK_IntegralComplex:
+ return getValueOfValueInit(
+ InitType->getAs<ComplexType>()->getElementType());
+ }
+ llvm_unreachable("Invalid scalar type kind");
+}
+
+static bool isZero(const Expr *E) {
+ switch (E->getStmtClass()) {
+ case Stmt::CXXNullPtrLiteralExprClass:
+ case Stmt::ImplicitValueInitExprClass:
+ return true;
+ case Stmt::InitListExprClass:
+ return cast<InitListExpr>(E)->getNumInits() == 0;
+ case Stmt::CharacterLiteralClass:
+ return !cast<CharacterLiteral>(E)->getValue();
+ case Stmt::CXXBoolLiteralExprClass:
+ return !cast<CXXBoolLiteralExpr>(E)->getValue();
+ case Stmt::IntegerLiteralClass:
+ return !cast<IntegerLiteral>(E)->getValue();
+ case Stmt::FloatingLiteralClass: {
+ llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
+ return Value.isZero() && !Value.isNegative();
+ }
+ default:
+ return false;
+ }
+}
+
+static const Expr *ignoreUnaryPlus(const Expr *E) {
+ auto *UnaryOp = dyn_cast<UnaryOperator>(E);
+ if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
+ return UnaryOp->getSubExpr();
+ return E;
+}
+
+static const Expr *getInitializer(const Expr *E) {
+ auto *InitList = dyn_cast<InitListExpr>(E);
+ if (InitList && InitList->getNumInits() == 1)
+ return InitList->getInit(0);
+ return E;
+}
+
+static bool sameValue(const Expr *E1, const Expr *E2) {
+ E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
+ E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
+
+ if (isZero(E1) && isZero(E2))
+ return true;
+
+ if (E1->getStmtClass() != E2->getStmtClass())
+ return false;
+
+ switch (E1->getStmtClass()) {
+ case Stmt::UnaryOperatorClass:
+ return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
+ cast<UnaryOperator>(E2)->getSubExpr());
+ case Stmt::CharacterLiteralClass:
+ return cast<CharacterLiteral>(E1)->getValue() ==
+ cast<CharacterLiteral>(E2)->getValue();
+ case Stmt::CXXBoolLiteralExprClass:
+ return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
+ cast<CXXBoolLiteralExpr>(E2)->getValue();
+ case Stmt::IntegerLiteralClass:
+ return cast<IntegerLiteral>(E1)->getValue() ==
+ cast<IntegerLiteral>(E2)->getValue();
+ case Stmt::FloatingLiteralClass:
+ return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
+ cast<FloatingLiteral>(E2)->getValue());
+ case Stmt::StringLiteralClass:
+ return cast<StringLiteral>(E1)->getString() ==
+ cast<StringLiteral>(E2)->getString();
+ case Stmt::DeclRefExprClass:
+ return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
+ default:
+ return false;
+ }
+}
+
+UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ UseAssignment(Options.get("UseAssignment", 0) != 0),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", 1) != 0) {}
+
+void UseDefaultMemberInitCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "UseAssignment", UseAssignment);
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ auto Init =
+ anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
+ unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+ hasUnaryOperand(integerLiteral())),
+ floatLiteral(),
+ unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+ hasUnaryOperand(floatLiteral())),
+ cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
+ declRefExpr(to(enumConstantDecl())));
+
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ isDefaultConstructor(), unless(isInstantiated()),
+ forEachConstructorInitializer(
+ allOf(forField(unless(anyOf(isBitField(),
+ hasInClassInitializer(anything())))),
+ cxxCtorInitializer(isWritten(),
+ withInitializer(ignoringImplicit(Init)))
+ .bind("default")))),
+ this);
+
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ unless(ast_matchers::isTemplateInstantiation()),
+ forEachConstructorInitializer(
+ allOf(forField(hasInClassInitializer(anything())),
+ cxxCtorInitializer(isWritten(),
+ withInitializer(ignoringImplicit(Init)))
+ .bind("existing")))),
+ this);
+}
+
+void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Default =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
+ checkDefaultInit(Result, Default);
+ else if (const auto *Existing =
+ Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
+ checkExistingInit(Result, Existing);
+ else
+ llvm_unreachable("Bad Callback. No node provided.");
+}
+
+void UseDefaultMemberInitCheck::checkDefaultInit(
+ const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+ const FieldDecl *Field = Init->getMember();
+
+ SourceLocation StartLoc = Field->getLocStart();
+ if (StartLoc.isMacroID() && IgnoreMacros)
+ return;
+
+ SourceLocation FieldEnd =
+ Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
+ *Result.SourceManager, getLangOpts());
+ SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
+ Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
+ CharSourceRange InitRange =
+ CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
+
+ auto Diag =
+ diag(Field->getLocation(), "use default member initializer for %0")
+ << Field
+ << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
+ << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
+
+ if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
+ Diag << FixItHint::CreateInsertion(
+ FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
+
+ if (!UseAssignment)
+ Diag << FixItHint::CreateInsertion(FieldEnd, "}");
+
+ Diag << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+void UseDefaultMemberInitCheck::checkExistingInit(
+ const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+ const FieldDecl *Field = Init->getMember();
+
+ if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
+ return;
+
+ diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
+ << Field
+ << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseDefaultMemberInitCheck.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_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Convert a default constructor's member initializers into default member
+/// initializers. Remove member initializers that are the same as a default
+/// member initializer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html
+class UseDefaultMemberInitCheck : public ClangTidyCheck {
+public:
+ UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void checkDefaultInit(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXCtorInitializer *Init);
+ void checkExistingInit(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXCtorInitializer *Init);
+
+ const bool UseAssignment;
+ const bool IgnoreMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
--- /dev/null
+//===--- UseEmplaceCheck.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 "UseEmplaceCheck.h"
+#include "../utils/OptionsUtils.h"
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
+ return Node.hasExplicitTemplateArgs();
+}
+
+const auto DefaultContainersWithPushBack =
+ "::std::vector; ::std::list; ::std::deque";
+const auto DefaultSmartPointers =
+ "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
+const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
+const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
+} // namespace
+
+UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ ContainersWithPushBack(utils::options::parseStringList(Options.get(
+ "ContainersWithPushBack", DefaultContainersWithPushBack))),
+ SmartPointers(utils::options::parseStringList(
+ Options.get("SmartPointers", DefaultSmartPointers))),
+ TupleTypes(utils::options::parseStringList(
+ Options.get("TupleTypes", DefaultTupleTypes))),
+ TupleMakeFunctions(utils::options::parseStringList(
+ Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))) {}
+
+void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ // FIXME: Bunch of functionality that could be easily added:
+ // + add handling of `push_front` for std::forward_list, std::list
+ // and std::deque.
+ // + add handling of `push` for std::stack, std::queue, std::priority_queue
+ // + add handling of `insert` for stl associative container, but be careful
+ // because this requires special treatment (it could cause performance
+ // regression)
+ // + match for emplace calls that should be replaced with insertion
+ auto CallPushBack = cxxMemberCallExpr(
+ hasDeclaration(functionDecl(hasName("push_back"))),
+ on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
+ ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
+
+ // We can't replace push_backs of smart pointer because
+ // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
+ // passed pointer because smart pointer won't be constructed
+ // (and destructed) as in push_back case.
+ auto IsCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
+ SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));
+
+ // Bitfields binds only to consts and emplace_back take it by universal ref.
+ auto BitFieldAsArgument = hasAnyArgument(
+ ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
+
+ // Initializer list can't be passed to universal reference.
+ auto InitializerListAsArgument = hasAnyArgument(
+ ignoringImplicit(cxxConstructExpr(isListInitialization())));
+
+ // We could have leak of resource.
+ auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
+ // We would call another constructor.
+ auto ConstructingDerived =
+ hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
+
+ // emplace_back can't access private constructor.
+ auto IsPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));
+
+ auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
+ has(cxxStdInitializerListExpr()));
+
+ // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
+ // overloaded functions and template names.
+ auto SoughtConstructExpr =
+ cxxConstructExpr(
+ unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
+ InitializerListAsArgument, NewExprAsArgument,
+ ConstructingDerived, IsPrivateCtor)))
+ .bind("ctor");
+ auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
+
+ auto MakeTuple = ignoringImplicit(
+ callExpr(
+ callee(expr(ignoringImplicit(declRefExpr(
+ unless(hasExplicitTemplateArgs()),
+ to(functionDecl(hasAnyName(SmallVector<StringRef, 2>(
+ TupleMakeFunctions.begin(), TupleMakeFunctions.end())))))))))
+ .bind("make"));
+
+ // make_something can return type convertible to container's element type.
+ // Allow the conversion only on containers of pairs.
+ auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
+ has(materializeTemporaryExpr(MakeTuple)),
+ hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
+ SmallVector<StringRef, 2>(TupleTypes.begin(), TupleTypes.end())))))));
+
+ auto SoughtParam = materializeTemporaryExpr(
+ anyOf(has(MakeTuple), has(MakeTupleCtor),
+ HasConstructExpr, has(cxxFunctionalCastExpr(HasConstructExpr))));
+
+ Finder->addMatcher(cxxMemberCallExpr(CallPushBack, has(SoughtParam),
+ unless(isInTemplateInstantiation()))
+ .bind("call"),
+ this);
+}
+
+void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
+ const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+ const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
+ assert((InnerCtorCall || MakeCall) && "No push_back parameter matched");
+
+ const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
+ Call->getExprLoc(), Call->getArg(0)->getExprLoc());
+
+ auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
+
+ if (FunctionNameSourceRange.getBegin().isMacroID())
+ return;
+
+ const auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
+ Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix);
+
+ const SourceRange CallParensRange =
+ MakeCall ? SourceRange(MakeCall->getCallee()->getLocEnd(),
+ MakeCall->getRParenLoc())
+ : InnerCtorCall->getParenOrBraceRange();
+
+ // Finish if there is no explicit constructor call.
+ if (CallParensRange.getBegin().isInvalid())
+ return;
+
+ const SourceLocation ExprBegin =
+ MakeCall ? MakeCall->getExprLoc() : InnerCtorCall->getExprLoc();
+
+ // Range for constructor name and opening brace.
+ const auto ParamCallSourceRange =
+ CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
+
+ Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
+ << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ CallParensRange.getEnd(), CallParensRange.getEnd()));
+}
+
+void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ContainersWithPushBack",
+ utils::options::serializeStringList(ContainersWithPushBack));
+ Options.store(Opts, "SmartPointers",
+ utils::options::serializeStringList(SmartPointers));
+ Options.store(Opts, "TupleTypes",
+ utils::options::serializeStringList(TupleTypes));
+ Options.store(Opts, "TupleMakeFunctions",
+ utils::options::serializeStringList(TupleMakeFunctions));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseEmplaceCheck.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_MODERNIZE_USE_EMPLACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
+
+#include "../ClangTidy.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check looks for cases when inserting new element into std::vector but
+/// the element is constructed temporarily.
+/// It replaces those calls for emplace_back of arguments passed to
+/// constructor of temporary object.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-emplace.html
+class UseEmplaceCheck : public ClangTidyCheck {
+public:
+ UseEmplaceCheck(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:
+ std::vector<std::string> ContainersWithPushBack;
+ std::vector<std::string> SmartPointers;
+ std::vector<std::string> TupleTypes;
+ std::vector<std::string> TupleMakeFunctions;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
--- /dev/null
+//===--- UseEqualsDefaultCheck.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 "UseEqualsDefaultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char SpecialFunction[] = "SpecialFunction";
+
+/// \brief Finds all the named non-static fields of \p Record.
+static std::set<const FieldDecl *>
+getAllNamedFields(const CXXRecordDecl *Record) {
+ std::set<const FieldDecl *> Result;
+ for (const auto *Field : Record->fields()) {
+ // Static data members are not in this range.
+ if (Field->isUnnamedBitfield())
+ continue;
+ Result.insert(Field);
+ }
+ return Result;
+}
+
+/// \brief Returns the names of the direct bases of \p Record, both virtual and
+/// non-virtual.
+static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
+ std::set<const Type *> Result;
+ for (auto Base : Record->bases()) {
+ // CXXBaseSpecifier.
+ const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
+ Result.insert(BaseType);
+ }
+ return Result;
+}
+
+/// \brief Returns a matcher that matches member expressions where the base is
+/// the variable declared as \p Var and the accessed member is the one declared
+/// as \p Field.
+internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
+ const ValueDecl *Var) {
+ return ignoringImpCasts(
+ memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
+ member(fieldDecl(equalsNode(Field)))));
+}
+
+/// \brief Check that the given constructor has copy signature and that it
+/// copy-initializes all its bases and members.
+static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
+ const CXXConstructorDecl *Ctor) {
+ // An explicitly-defaulted constructor cannot have default arguments.
+ if (Ctor->getMinRequiredArguments() != 1)
+ return false;
+
+ const auto *Record = Ctor->getParent();
+ const auto *Param = Ctor->getParamDecl(0);
+
+ // Base classes and members that have to be copied.
+ auto BasesToInit = getAllDirectBases(Record);
+ auto FieldsToInit = getAllNamedFields(Record);
+
+ // Ensure that all the bases are copied.
+ for (const auto *Base : BasesToInit) {
+ // The initialization of a base class should be a call to a copy
+ // constructor of the base.
+ if (match(
+ cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+ isBaseInitializer(),
+ withInitializer(cxxConstructExpr(allOf(
+ hasType(equalsNode(Base)),
+ hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+ argumentCountIs(1),
+ hasArgument(
+ 0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
+ *Ctor, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that all the members are copied.
+ for (const auto *Field : FieldsToInit) {
+ auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
+ // The initialization is a CXXConstructExpr for class types.
+ if (match(
+ cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+ isMemberInitializer(), forField(equalsNode(Field)),
+ withInitializer(anyOf(
+ AccessToFieldInParam,
+ cxxConstructExpr(allOf(
+ hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+ argumentCountIs(1),
+ hasArgument(0, AccessToFieldInParam)))))))),
+ *Ctor, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that we don't do anything else, like initializing an indirect base.
+ return Ctor->getNumCtorInitializers() ==
+ BasesToInit.size() + FieldsToInit.size();
+}
+
+/// \brief Checks that the given method is an overloading of the assignment
+/// operator, has copy signature, returns a reference to "*this" and copies
+/// all its members and subobjects.
+static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
+ const CXXMethodDecl *Operator) {
+ const auto *Record = Operator->getParent();
+ const auto *Param = Operator->getParamDecl(0);
+
+ // Base classes and members that have to be copied.
+ auto BasesToInit = getAllDirectBases(Record);
+ auto FieldsToInit = getAllNamedFields(Record);
+
+ const auto *Compound = cast<CompoundStmt>(Operator->getBody());
+
+ // The assignment operator definition has to end with the following return
+ // statement:
+ // return *this;
+ if (Compound->body_empty() ||
+ match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
+ hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
+ *Compound->body_back(), *Context)
+ .empty())
+ return false;
+
+ // Ensure that all the bases are copied.
+ for (const auto *Base : BasesToInit) {
+ // Assignment operator of a base class:
+ // Base::operator=(Other);
+ //
+ // Clang translates this into:
+ // ((Base*)this)->operator=((Base)Other);
+ //
+ // So we are looking for a member call that fulfills:
+ if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
+ // - The object is an implicit cast of 'this' to a pointer to
+ // a base class.
+ onImplicitObjectArgument(
+ implicitCastExpr(hasImplicitDestinationType(
+ pointsTo(type(equalsNode(Base)))),
+ hasSourceExpression(cxxThisExpr()))),
+ // - The called method is the operator=.
+ callee(cxxMethodDecl(isCopyAssignmentOperator())),
+ // - The argument is (an implicit cast to a Base of) the
+ // argument taken by "Operator".
+ argumentCountIs(1),
+ hasArgument(0,
+ declRefExpr(to(varDecl(equalsNode(Param)))))))))),
+ *Compound, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that all the members are copied.
+ for (const auto *Field : FieldsToInit) {
+ // The assignment of data members:
+ // Field = Other.Field;
+ // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
+ // otherwise.
+ auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
+ member(fieldDecl(equalsNode(Field))));
+ auto RHS = accessToFieldInVar(Field, Param);
+ if (match(
+ compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
+ binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
+ cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+ argumentCountIs(2), hasArgument(0, LHS),
+ hasArgument(1, RHS))))))),
+ *Compound, *Context)
+ .empty())
+ return false;
+ }
+
+ // Ensure that we don't do anything else.
+ return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
+}
+
+/// \brief Returns false if the body has any non-whitespace character.
+static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
+ bool Invalid = false;
+ StringRef Text = Lexer::getSourceText(
+ CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
+ Body->getRBracLoc()),
+ Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+ return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
+}
+
+void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
+ if (getLangOpts().CPlusPlus) {
+ // Destructor.
+ Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
+ this);
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ isDefinition(),
+ anyOf(
+ // Default constructor.
+ allOf(unless(hasAnyConstructorInitializer(isWritten())),
+ parameterCountIs(0)),
+ // Copy constructor.
+ allOf(isCopyConstructor(),
+ // Discard constructors that can be used as a copy
+ // constructor because all the other arguments have
+ // default values.
+ parameterCountIs(1))))
+ .bind(SpecialFunction),
+ this);
+ // Copy-assignment operator.
+ Finder->addMatcher(
+ cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
+ // isCopyAssignmentOperator() allows the parameter to be
+ // passed by value, and in this case it cannot be
+ // defaulted.
+ hasParameter(0, hasType(lValueReferenceType())))
+ .bind(SpecialFunction),
+ this);
+ }
+}
+
+void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+ std::string SpecialFunctionName;
+
+ // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
+ const auto *SpecialFunctionDecl =
+ Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
+
+ // Discard explicitly deleted/defaulted special member functions and those
+ // that are not user-provided (automatically generated).
+ if (SpecialFunctionDecl->isDeleted() ||
+ SpecialFunctionDecl->isExplicitlyDefaulted() ||
+ SpecialFunctionDecl->isLateTemplateParsed() ||
+ SpecialFunctionDecl->isTemplateInstantiation() ||
+ !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
+ return;
+
+ const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
+ if (!Body)
+ return;
+
+ // If there is code inside the body, don't warn.
+ if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
+ return;
+
+ // If there are comments inside the body, don't do the change.
+ bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
+ bodyEmpty(Result.Context, Body);
+
+ std::vector<FixItHint> RemoveInitializers;
+
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
+ if (Ctor->getNumParams() == 0) {
+ SpecialFunctionName = "default constructor";
+ } else {
+ if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
+ return;
+ SpecialFunctionName = "copy constructor";
+ // If there are constructor initializers, they must be removed.
+ for (const auto *Init : Ctor->inits()) {
+ RemoveInitializers.emplace_back(
+ FixItHint::CreateRemoval(Init->getSourceRange()));
+ }
+ }
+ } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
+ SpecialFunctionName = "destructor";
+ } else {
+ if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
+ return;
+ SpecialFunctionName = "copy-assignment operator";
+ }
+
+ // The location of the body is more useful inside a macro as spelling and
+ // expansion locations are reported.
+ SourceLocation Location = SpecialFunctionDecl->getLocation();
+ if (Location.isMacroID())
+ Location = Body->getLocStart();
+
+ auto Diag = diag(Location, "use '= default' to define a trivial " +
+ SpecialFunctionName);
+
+ if (ApplyFix)
+ Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;")
+ << RemoveInitializers;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseEqualsDefaultCheck.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_MODERNIZE_USE_EQUALS_DEFAULT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Replace default bodies of special member functions with '= default;'.
+/// \code
+/// struct A {
+/// A() {}
+/// ~A();
+/// };
+/// A::~A() {}
+/// \endcode
+/// Is converted to:
+/// \code
+/// struct A {
+/// A() = default;
+/// ~A();
+/// };
+/// A::~A() = default;
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html
+class UseEqualsDefaultCheck : public ClangTidyCheck {
+public:
+ UseEqualsDefaultCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H
--- /dev/null
+//===--- UseEqualsDeleteCheck.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 "UseEqualsDeleteCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char SpecialFunction[] = "SpecialFunction";
+static const char DeletedNotPublic[] = "DeletedNotPublic";
+
+void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto PrivateSpecialFn = cxxMethodDecl(
+ isPrivate(),
+ anyOf(cxxConstructorDecl(anyOf(isDefaultConstructor(),
+ isCopyConstructor(), isMoveConstructor())),
+ cxxMethodDecl(
+ anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())),
+ cxxDestructorDecl()));
+
+ Finder->addMatcher(
+ cxxMethodDecl(
+ PrivateSpecialFn,
+ unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(),
+ ast_matchers::isTemplateInstantiation(),
+ // Ensure that all methods except private special member
+ // functions are defined.
+ hasParent(cxxRecordDecl(hasMethod(unless(
+ anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(),
+ isDefaulted(), isDeleted()))))))))
+ .bind(SpecialFunction),
+ this);
+
+ Finder->addMatcher(
+ cxxMethodDecl(isDeleted(), unless(isPublic())).bind(DeletedNotPublic),
+ this);
+}
+
+void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Func =
+ Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction)) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Func->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+
+ // FIXME: Improve FixItHint to make the method public.
+ diag(Func->getLocation(),
+ "use '= delete' to prohibit calling of a special member function")
+ << FixItHint::CreateInsertion(EndLoc, " = delete");
+ } else if (const auto *Func =
+ Result.Nodes.getNodeAs<CXXMethodDecl>(DeletedNotPublic)) {
+ // Ignore this warning in macros, since it's extremely noisy in code using
+ // DISALLOW_COPY_AND_ASSIGN-style macros and there's no easy way to
+ // automatically fix the warning when macros are in play.
+ if (Func->getLocation().isMacroID())
+ return;
+ // FIXME: Add FixItHint to make the method public.
+ diag(Func->getLocation(), "deleted member function should be public");
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseEqualsDeleteCheck.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_MODERNIZE_USE_EQUALS_DELETE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Mark unimplemented private special member functions with '= delete'.
+/// \code
+/// struct A {
+/// private:
+/// A(const A&);
+/// A& operator=(const A&);
+/// };
+/// \endcode
+/// Is converted to:
+/// \code
+/// struct A {
+/// private:
+/// A(const A&) = delete;
+/// A& operator=(const A&) = delete;
+/// };
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html
+class UseEqualsDeleteCheck : public ClangTidyCheck {
+public:
+ UseEqualsDeleteCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
--- /dev/null
+//===--- UseNoexceptCheck.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 "UseNoexceptCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NoexceptMacro(Options.get("ReplacementString", "")),
+ UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {}
+
+void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ReplacementString", NoexceptMacro);
+ Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse);
+}
+
+void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+
+ Finder->addMatcher(
+ functionDecl(
+ cxxMethodDecl(
+ hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
+ anyOf(hasOverloadedOperatorName("delete[]"),
+ hasOverloadedOperatorName("delete"), cxxDestructorDecl()))
+ .bind("del-dtor"))
+ .bind("funcDecl"),
+ this);
+
+ Finder->addMatcher(
+ functionDecl(
+ hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
+ unless(anyOf(hasOverloadedOperatorName("delete[]"),
+ hasOverloadedOperatorName("delete"),
+ cxxDestructorDecl())))
+ .bind("funcDecl"),
+ this);
+
+ Finder->addMatcher(
+ parmVarDecl(anyOf(hasType(pointerType(pointee(parenType(innerType(
+ functionProtoType(hasDynamicExceptionSpec())))))),
+ hasType(memberPointerType(pointee(parenType(innerType(
+ functionProtoType(hasDynamicExceptionSpec()))))))))
+ .bind("parmVarDecl"),
+ this);
+}
+
+void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
+ const FunctionProtoType *FnTy = nullptr;
+ bool DtorOrOperatorDel = false;
+ SourceRange Range;
+
+ if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl")) {
+ DtorOrOperatorDel = Result.Nodes.getNodeAs<FunctionDecl>("del-dtor");
+ FnTy = FuncDecl->getType()->getAs<FunctionProtoType>();
+ if (const auto *TSI = FuncDecl->getTypeSourceInfo())
+ Range =
+ TSI->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange();
+ } else if (const auto *ParmDecl =
+ Result.Nodes.getNodeAs<ParmVarDecl>("parmVarDecl")) {
+ FnTy = ParmDecl->getType()
+ ->getAs<Type>()
+ ->getPointeeType()
+ ->getAs<FunctionProtoType>();
+
+ if (const auto *TSI = ParmDecl->getTypeSourceInfo())
+ Range = TSI->getTypeLoc()
+ .getNextTypeLoc()
+ .IgnoreParens()
+ .castAs<FunctionProtoTypeLoc>()
+ .getExceptionSpecRange();
+ }
+ CharSourceRange CRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(Range), *Result.SourceManager,
+ Result.Context->getLangOpts());
+
+ assert(FnTy && "FunctionProtoType is null.");
+ bool IsNoThrow = FnTy->isNothrow(*Result.Context);
+ StringRef ReplacementStr =
+ IsNoThrow
+ ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str()
+ : NoexceptMacro.empty()
+ ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)"
+ : ""
+ : "";
+
+ FixItHint FixIt;
+ if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid())
+ FixIt = FixItHint::CreateReplacement(CRange, ReplacementStr);
+
+ diag(Range.getBegin(), "dynamic exception specification '%0' is deprecated; "
+ "consider %select{using '%2'|removing it}1 instead")
+ << Lexer::getSourceText(CRange, *Result.SourceManager,
+ Result.Context->getLangOpts())
+ << ReplacementStr.empty() << ReplacementStr << FixIt;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseNoexceptCheck.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_MODERNIZE_USE_NOEXCEPT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Replace dynamic exception specifications, with
+/// `noexcept` (or user-defined macro) or `noexcept(false)`.
+/// \code
+/// void foo() throw();
+/// void bar() throw(int);
+/// \endcode
+/// Is converted to:
+/// \code
+/// void foo() ;
+/// void bar() noexcept(false);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html
+class UseNoexceptCheck : public ClangTidyCheck {
+public:
+ UseNoexceptCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const std::string NoexceptMacro;
+ bool UseNoexceptFalse;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
--- /dev/null
+//===--- UseNullptrCheck.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 "UseNullptrCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+const char CastSequence[] = "sequence";
+
+AST_MATCHER(Type, sugaredNullptrType) {
+ const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
+ if (const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
+ return BT->getKind() == BuiltinType::NullPtr;
+ return false;
+}
+
+/// \brief Create a matcher that finds implicit casts as well as the head of a
+/// sequence of zero or more nested explicit casts that have an implicit cast
+/// to null within.
+/// Finding sequences of explict casts is necessary so that an entire sequence
+/// can be replaced instead of just the inner-most implicit cast.
+StatementMatcher makeCastSequenceMatcher() {
+ StatementMatcher ImplicitCastToNull = implicitCastExpr(
+ anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
+ unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
+ unless(hasSourceExpression(hasType(sugaredNullptrType()))));
+
+ return castExpr(anyOf(ImplicitCastToNull,
+ explicitCastExpr(hasDescendant(ImplicitCastToNull))),
+ unless(hasAncestor(explicitCastExpr())))
+ .bind(CastSequence);
+}
+
+bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
+ const SourceManager &SM) {
+ return SM.isWrittenInSameFile(StartLoc, EndLoc);
+}
+
+/// \brief Replaces the provided range with the text "nullptr", but only if
+/// the start and end location are both in main file.
+/// Returns true if and only if a replacement was made.
+void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
+ SourceLocation StartLoc, SourceLocation EndLoc) {
+ CharSourceRange Range(SourceRange(StartLoc, EndLoc), true);
+ // Add a space if nullptr follows an alphanumeric character. This happens
+ // whenever there is an c-style explicit cast to nullptr not surrounded by
+ // parentheses and right beside a return statement.
+ SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
+ bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
+ Check.diag(Range.getBegin(), "use nullptr") << FixItHint::CreateReplacement(
+ Range, NeedsSpace ? " nullptr" : "nullptr");
+}
+
+/// \brief Returns the name of the outermost macro.
+///
+/// Given
+/// \code
+/// #define MY_NULL NULL
+/// \endcode
+/// If \p Loc points to NULL, this function will return the name MY_NULL.
+StringRef getOutermostMacroName(SourceLocation Loc, const SourceManager &SM,
+ const LangOptions &LO) {
+ assert(Loc.isMacroID());
+ SourceLocation OutermostMacroLoc;
+
+ while (Loc.isMacroID()) {
+ OutermostMacroLoc = Loc;
+ Loc = SM.getImmediateMacroCallerLoc(Loc);
+ }
+
+ return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
+}
+
+/// \brief RecursiveASTVisitor for ensuring all nodes rooted at a given AST
+/// subtree that have file-level source locations corresponding to a macro
+/// argument have implicit NullTo(Member)Pointer nodes as ancestors.
+class MacroArgUsageVisitor : public RecursiveASTVisitor<MacroArgUsageVisitor> {
+public:
+ MacroArgUsageVisitor(SourceLocation CastLoc, const SourceManager &SM)
+ : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
+ InvalidFound(false) {
+ assert(CastLoc.isFileID());
+ }
+
+ bool TraverseStmt(Stmt *S) {
+ bool VisitedPreviously = Visited;
+
+ if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
+ return false;
+
+ // The point at which VisitedPreviously is false and Visited is true is the
+ // root of a subtree containing nodes whose locations match CastLoc. It's
+ // at this point we test that the Implicit NullTo(Member)Pointer cast was
+ // found or not.
+ if (!VisitedPreviously) {
+ if (Visited && !CastFound) {
+ // Found nodes with matching SourceLocations but didn't come across a
+ // cast. This is an invalid macro arg use. Can stop traversal
+ // completely now.
+ InvalidFound = true;
+ return false;
+ }
+ // Reset state as we unwind back up the tree.
+ CastFound = false;
+ Visited = false;
+ }
+ return true;
+ }
+
+ bool VisitStmt(Stmt *S) {
+ if (SM.getFileLoc(S->getLocStart()) != CastLoc)
+ return true;
+ Visited = true;
+
+ const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
+ if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
+ Cast->getCastKind() == CK_NullToMemberPointer))
+ CastFound = true;
+
+ return true;
+ }
+
+ bool TraverseInitListExpr(InitListExpr *S) {
+ // Only go through the semantic form of the InitListExpr, because
+ // ImplicitCast might not appear in the syntactic form, and this results in
+ // finding usages of the macro argument that don't have a ImplicitCast as an
+ // ancestor (thus invalidating the replacement) when they actually have.
+ return RecursiveASTVisitor<MacroArgUsageVisitor>::
+ TraverseSynOrSemInitListExpr(
+ S->isSemanticForm() ? S : S->getSemanticForm());
+ }
+
+ bool foundInvalid() const { return InvalidFound; }
+
+private:
+ SourceLocation CastLoc;
+ const SourceManager &SM;
+
+ bool Visited;
+ bool CastFound;
+ bool InvalidFound;
+};
+
+/// \brief Looks for implicit casts as well as sequences of 0 or more explicit
+/// casts with an implicit null-to-pointer cast within.
+///
+/// The matcher this visitor is used with will find a single implicit cast or a
+/// top-most explicit cast (i.e. it has no explicit casts as an ancestor) where
+/// an implicit cast is nested within. However, there is no guarantee that only
+/// explicit casts exist between the found top-most explicit cast and the
+/// possibly more than one nested implicit cast. This visitor finds all cast
+/// sequences with an implicit cast to null within and creates a replacement
+/// leaving the outermost explicit cast unchanged to avoid introducing
+/// ambiguities.
+class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
+public:
+ CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
+ ClangTidyCheck &check)
+ : SM(Context.getSourceManager()), Context(Context),
+ NullMacros(NullMacros), Check(check), FirstSubExpr(nullptr),
+ PruneSubtree(false) {}
+
+ bool TraverseStmt(Stmt *S) {
+ // Stop traversing down the tree if requested.
+ if (PruneSubtree) {
+ PruneSubtree = false;
+ return true;
+ }
+ return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
+ }
+
+ // Only VisitStmt is overridden as we shouldn't find other base AST types
+ // within a cast expression.
+ bool VisitStmt(Stmt *S) {
+ auto *C = dyn_cast<CastExpr>(S);
+ // Catch the castExpr inside cxxDefaultArgExpr.
+ if (auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
+ C = dyn_cast<CastExpr>(E->getExpr());
+ FirstSubExpr = nullptr;
+ }
+ if (!C) {
+ FirstSubExpr = nullptr;
+ return true;
+ }
+
+ auto* CastSubExpr = C->getSubExpr()->IgnoreParens();
+ // Ignore cast expressions which cast nullptr literal.
+ if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
+ return true;
+ }
+
+ if (!FirstSubExpr)
+ FirstSubExpr = CastSubExpr;
+
+ if (C->getCastKind() != CK_NullToPointer &&
+ C->getCastKind() != CK_NullToMemberPointer) {
+ return true;
+ }
+
+ SourceLocation StartLoc = FirstSubExpr->getLocStart();
+ SourceLocation EndLoc = FirstSubExpr->getLocEnd();
+
+ // If the location comes from a macro arg expansion, *all* uses of that
+ // arg must be checked to result in NullTo(Member)Pointer casts.
+ //
+ // If the location comes from a macro body expansion, check to see if its
+ // coming from one of the allowed 'NULL' macros.
+ if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
+ SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
+ FileLocEnd = SM.getFileLoc(EndLoc);
+ SourceLocation ImmediateMarcoArgLoc, MacroLoc;
+ // Skip NULL macros used in macro.
+ if (!getMacroAndArgLocations(StartLoc, ImmediateMarcoArgLoc, MacroLoc) ||
+ ImmediateMarcoArgLoc != FileLocStart)
+ return skipSubTree();
+
+ if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
+ allArgUsesValid(C)) {
+ replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
+ }
+ return true;
+ }
+
+ if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
+ StringRef OutermostMacroName =
+ getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
+
+ // Check to see if the user wants to replace the macro being expanded.
+ if (std::find(NullMacros.begin(), NullMacros.end(), OutermostMacroName) ==
+ NullMacros.end()) {
+ return skipSubTree();
+ }
+
+ StartLoc = SM.getFileLoc(StartLoc);
+ EndLoc = SM.getFileLoc(EndLoc);
+ }
+
+ if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
+ return skipSubTree();
+ }
+ replaceWithNullptr(Check, SM, StartLoc, EndLoc);
+
+ return true;
+ }
+
+private:
+ bool skipSubTree() {
+ PruneSubtree = true;
+ return true;
+ }
+
+ /// \brief Tests that all expansions of a macro arg, one of which expands to
+ /// result in \p CE, yield NullTo(Member)Pointer casts.
+ bool allArgUsesValid(const CastExpr *CE) {
+ SourceLocation CastLoc = CE->getLocStart();
+
+ // Step 1: Get location of macro arg and location of the macro the arg was
+ // provided to.
+ SourceLocation ArgLoc, MacroLoc;
+ if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
+ return false;
+
+ // Step 2: Find the first ancestor that doesn't expand from this macro.
+ ast_type_traits::DynTypedNode ContainingAncestor;
+ if (!findContainingAncestor(
+ ast_type_traits::DynTypedNode::create<Stmt>(*CE), MacroLoc,
+ ContainingAncestor))
+ return false;
+
+ // Step 3:
+ // Visit children of this containing parent looking for the least-descended
+ // nodes of the containing parent which are macro arg expansions that expand
+ // from the given arg location.
+ // Visitor needs: arg loc.
+ MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
+ if (const auto *D = ContainingAncestor.get<Decl>())
+ ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
+ else if (const auto *S = ContainingAncestor.get<Stmt>())
+ ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
+ else
+ llvm_unreachable("Unhandled ContainingAncestor node type");
+
+ return !ArgUsageVisitor.foundInvalid();
+ }
+
+ /// \brief Given the SourceLocation for a macro arg expansion, finds the
+ /// non-macro SourceLocation of the macro the arg was passed to and the
+ /// non-macro SourceLocation of the argument in the arg list to that macro.
+ /// These results are returned via \c MacroLoc and \c ArgLoc respectively.
+ /// These values are undefined if the return value is false.
+ ///
+ /// \returns false if one of the returned SourceLocations would be a
+ /// SourceLocation pointing within the definition of another macro.
+ bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
+ SourceLocation &MacroLoc) {
+ assert(Loc.isMacroID() && "Only reasonble to call this on macros");
+
+ ArgLoc = Loc;
+
+ // Find the location of the immediate macro expansion.
+ while (true) {
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
+ const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
+ const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
+
+ SourceLocation OldArgLoc = ArgLoc;
+ ArgLoc = Expansion.getExpansionLocStart();
+ if (!Expansion.isMacroArgExpansion()) {
+ if (!MacroLoc.isFileID())
+ return false;
+
+ StringRef Name =
+ Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
+ return std::find(NullMacros.begin(), NullMacros.end(), Name) !=
+ NullMacros.end();
+ }
+
+ MacroLoc = SM.getExpansionRange(ArgLoc).first;
+
+ ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
+ if (ArgLoc.isFileID())
+ return true;
+
+ // If spelling location resides in the same FileID as macro expansion
+ // location, it means there is no inner macro.
+ FileID MacroFID = SM.getFileID(MacroLoc);
+ if (SM.isInFileID(ArgLoc, MacroFID)) {
+ // Don't transform this case. If the characters that caused the
+ // null-conversion come from within a macro, they can't be changed.
+ return false;
+ }
+ }
+
+ llvm_unreachable("getMacroAndArgLocations");
+ }
+
+ /// \brief Tests if TestMacroLoc is found while recursively unravelling
+ /// expansions starting at TestLoc. TestMacroLoc.isFileID() must be true.
+ /// Implementation is very similar to getMacroAndArgLocations() except in this
+ /// case, it's not assumed that TestLoc is expanded from a macro argument.
+ /// While unravelling expansions macro arguments are handled as with
+ /// getMacroAndArgLocations() but in this function macro body expansions are
+ /// also handled.
+ ///
+ /// False means either:
+ /// - TestLoc is not from a macro expansion.
+ /// - TestLoc is from a different macro expansion.
+ bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
+ if (TestLoc.isFileID()) {
+ return false;
+ }
+
+ SourceLocation Loc = TestLoc, MacroLoc;
+
+ while (true) {
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+ const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
+ const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
+
+ Loc = Expansion.getExpansionLocStart();
+
+ if (!Expansion.isMacroArgExpansion()) {
+ if (Loc.isFileID()) {
+ return Loc == TestMacroLoc;
+ }
+ // Since Loc is still a macro ID and it's not an argument expansion, we
+ // don't need to do the work of handling an argument expansion. Simply
+ // keep recursively expanding until we hit a FileID or a macro arg
+ // expansion or a macro arg expansion.
+ continue;
+ }
+
+ MacroLoc = SM.getImmediateExpansionRange(Loc).first;
+ if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
+ // Match made.
+ return true;
+ }
+
+ Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
+ if (Loc.isFileID()) {
+ // If we made it this far without finding a match, there is no match to
+ // be made.
+ return false;
+ }
+ }
+
+ llvm_unreachable("expandsFrom");
+ }
+
+ /// \brief Given a starting point \c Start in the AST, find an ancestor that
+ /// doesn't expand from the macro called at file location \c MacroLoc.
+ ///
+ /// \pre MacroLoc.isFileID()
+ /// \returns true if such an ancestor was found, false otherwise.
+ bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
+ SourceLocation MacroLoc,
+ ast_type_traits::DynTypedNode &Result) {
+ // Below we're only following the first parent back up the AST. This should
+ // be fine since for the statements we care about there should only be one
+ // parent, except for the case specified below.
+
+ assert(MacroLoc.isFileID());
+
+ while (true) {
+ const auto &Parents = Context.getParents(Start);
+ if (Parents.empty())
+ return false;
+ if (Parents.size() > 1) {
+ // If there are more than one parents, don't do the replacement unless
+ // they are InitListsExpr (semantic and syntactic form). In this case we
+ // can choose any one here, and the ASTVisitor will take care of
+ // traversing the right one.
+ for (const auto &Parent : Parents) {
+ if (!Parent.get<InitListExpr>())
+ return false;
+ }
+ }
+
+ const ast_type_traits::DynTypedNode &Parent = Parents[0];
+
+ SourceLocation Loc;
+ if (const auto *D = Parent.get<Decl>())
+ Loc = D->getLocStart();
+ else if (const auto *S = Parent.get<Stmt>())
+ Loc = S->getLocStart();
+
+ // TypeLoc and NestedNameSpecifierLoc are members of the parent map. Skip
+ // them and keep going up.
+ if (Loc.isValid()) {
+ if (!expandsFrom(Loc, MacroLoc)) {
+ Result = Parent;
+ return true;
+ }
+ }
+ Start = Parent;
+ }
+
+ llvm_unreachable("findContainingAncestor");
+ }
+
+private:
+ SourceManager &SM;
+ ASTContext &Context;
+ ArrayRef<StringRef> NullMacros;
+ ClangTidyCheck &Check;
+ Expr *FirstSubExpr;
+ bool PruneSubtree;
+};
+
+} // namespace
+
+UseNullptrCheck::UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NullMacrosStr(Options.get("NullMacros", "")) {
+ StringRef(NullMacrosStr).split(NullMacros, ",");
+}
+
+void UseNullptrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "NullMacros", NullMacrosStr);
+}
+
+void UseNullptrCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matcher for C++. Because this checker is used for
+ // modernization, it is reasonable to run it on any C++ standard with the
+ // assumption the user is trying to modernize their codebase.
+ if (getLangOpts().CPlusPlus)
+ Finder->addMatcher(makeCastSequenceMatcher(), this);
+}
+
+void UseNullptrCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
+ assert(NullCast && "Bad Callback. No node provided");
+
+ // Given an implicit null-ptr cast or an explicit cast with an implicit
+ // null-to-pointer cast within use CastSequenceVisitor to identify sequences
+ // of explicit casts that can be converted into 'nullptr'.
+ CastSequenceVisitor(*Result.Context, NullMacros, *this)
+ .TraverseStmt(const_cast<CastExpr *>(NullCast));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseNullptrCheck.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_MODERNIZE_USE_NULLPTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NULLPTR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class UseNullptrCheck : public ClangTidyCheck {
+public:
+ UseNullptrCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const std::string NullMacrosStr;
+ SmallVector<StringRef, 1> NullMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NULLPTR_H
--- /dev/null
+//===--- UseOverrideCheck.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 "UseOverrideCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matcher for C++11.
+ if (getLangOpts().CPlusPlus11)
+ Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
+}
+
+// Re-lex the tokens to get precise locations to insert 'override' and remove
+// 'virtual'.
+static SmallVector<Token, 16>
+ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
+ const SourceManager &Sources = *Result.SourceManager;
+ std::pair<FileID, unsigned> LocInfo =
+ Sources.getDecomposedLoc(Range.getBegin());
+ StringRef File = Sources.getBufferData(LocInfo.first);
+ const char *TokenBegin = File.data() + LocInfo.second;
+ Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
+ Result.Context->getLangOpts(), File.begin(), TokenBegin,
+ File.end());
+ SmallVector<Token, 16> Tokens;
+ Token Tok;
+ int NestedParens = 0;
+ while (!RawLexer.LexFromRawLexer(Tok)) {
+ if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
+ break;
+ if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
+ break;
+ if (Tok.is(tok::l_paren))
+ ++NestedParens;
+ else if (Tok.is(tok::r_paren))
+ --NestedParens;
+ if (Tok.is(tok::raw_identifier)) {
+ IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
+ Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
+ Tok.setIdentifierInfo(&Info);
+ Tok.setKind(Info.getTokenID());
+ }
+ Tokens.push_back(Tok);
+ }
+ return Tokens;
+}
+
+static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
+ return StringRef(Sources.getCharacterData(Tok.getLocation()),
+ Tok.getLength());
+}
+
+void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
+ const SourceManager &Sources = *Result.SourceManager;
+
+ assert(Method != nullptr);
+ if (Method->getInstantiatedFromMemberFunction() != nullptr)
+ Method = Method->getInstantiatedFromMemberFunction();
+
+ if (Method->isImplicit() || Method->getLocation().isMacroID() ||
+ Method->isOutOfLine())
+ return;
+
+ bool HasVirtual = Method->isVirtualAsWritten();
+ bool HasOverride = Method->getAttr<OverrideAttr>();
+ bool HasFinal = Method->getAttr<FinalAttr>();
+
+ bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
+ unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
+
+ if (!OnlyVirtualSpecified && KeywordCount == 1)
+ return; // Nothing to do.
+
+ std::string Message;
+
+ if (OnlyVirtualSpecified) {
+ Message =
+ "prefer using 'override' or (rarely) 'final' instead of 'virtual'";
+ } else if (KeywordCount == 0) {
+ Message = "annotate this function with 'override' or (rarely) 'final'";
+ } else {
+ StringRef Redundant =
+ HasVirtual ? (HasOverride && HasFinal ? "'virtual' and 'override' are"
+ : "'virtual' is")
+ : "'override' is";
+ StringRef Correct = HasFinal ? "'final'" : "'override'";
+
+ Message = (llvm::Twine(Redundant) +
+ " redundant since the function is already declared " + Correct)
+ .str();
+ }
+
+ DiagnosticBuilder Diag = diag(Method->getLocation(), Message);
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
+ getLangOpts());
+
+ if (!FileRange.isValid())
+ return;
+
+ // FIXME: Instead of re-lexing and looking for specific macros such as
+ // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
+ // FunctionDecl.
+ SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result);
+
+ // Add 'override' on inline declarations that don't already have it.
+ if (!HasFinal && !HasOverride) {
+ SourceLocation InsertLoc;
+ StringRef ReplacementText = "override ";
+ SourceLocation MethodLoc = Method->getLocation();
+
+ for (Token T : Tokens) {
+ if (T.is(tok::kw___attribute) &&
+ !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
+ InsertLoc = T.getLocation();
+ break;
+ }
+ }
+
+ if (Method->hasAttrs()) {
+ for (const clang::Attr *A : Method->getAttrs()) {
+ if (!A->isImplicit() && !A->isInherited()) {
+ SourceLocation Loc =
+ Sources.getExpansionLoc(A->getRange().getBegin());
+ if ((!InsertLoc.isValid() ||
+ Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
+ !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
+ InsertLoc = Loc;
+ }
+ }
+ }
+
+ if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
+ Method->getBody() && !Method->isDefaulted()) {
+ // For methods with inline definition, add the override keyword at the
+ // end of the declaration of the function, but prefer to put it on the
+ // same line as the declaration if the beginning brace for the start of
+ // the body falls on the next line.
+ ReplacementText = " override";
+ auto LastTokenIter = std::prev(Tokens.end());
+ // When try statement is used instead of compound statement as
+ // method body - insert override keyword before it.
+ if (LastTokenIter->is(tok::kw_try))
+ LastTokenIter = std::prev(LastTokenIter);
+ InsertLoc = LastTokenIter->getEndLoc();
+ }
+
+ if (!InsertLoc.isValid()) {
+ // For declarations marked with "= 0" or "= [default|delete]", the end
+ // location will point until after those markings. Therefore, the override
+ // keyword shouldn't be inserted at the end, but before the '='.
+ if (Tokens.size() > 2 && (GetText(Tokens.back(), Sources) == "0" ||
+ Tokens.back().is(tok::kw_default) ||
+ Tokens.back().is(tok::kw_delete)) &&
+ GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
+ InsertLoc = Tokens[Tokens.size() - 2].getLocation();
+ // Check if we need to insert a space.
+ if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
+ ReplacementText = " override ";
+ } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") {
+ InsertLoc = Tokens.back().getLocation();
+ }
+ }
+
+ if (!InsertLoc.isValid()) {
+ InsertLoc = FileRange.getEnd();
+ ReplacementText = " override";
+ }
+ Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
+ }
+
+ if (HasFinal && HasOverride) {
+ SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
+ }
+
+ if (HasVirtual) {
+ for (Token Tok : Tokens) {
+ if (Tok.is(tok::kw_virtual)) {
+ Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ Tok.getLocation(), Tok.getLocation()));
+ break;
+ }
+ }
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseOverrideCheck.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_MODERNIZE_USEOVERRIDECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEOVERRIDECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Use C++11's `override` and remove `virtual` where applicable.
+class UseOverrideCheck : public ClangTidyCheck {
+public:
+ UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEOVERRIDECHECK_H
--- /dev/null
+//===--- UseTransparentFunctorsCheck.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 "UseTransparentFunctorsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {}
+
+void UseTransparentFunctorsCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "SafeMode", SafeMode ? 1 : 0);
+}
+
+void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus14)
+ return;
+
+ const auto TransparentFunctors =
+ classTemplateSpecializationDecl(
+ unless(hasAnyTemplateArgument(refersToType(voidType()))),
+ hasAnyName("::std::plus", "::std::minus", "::std::multiplies",
+ "::std::divides", "::std::modulus", "::std::negate",
+ "::std::equal_to", "::std::not_equal_to", "::std::greater",
+ "::std::less", "::std::greater_equal", "::std::less_equal",
+ "::std::logical_and", "::std::logical_or",
+ "::std::logical_not", "::std::bit_and", "::std::bit_or",
+ "::std::bit_xor", "::std::bit_not"))
+ .bind("FunctorClass");
+
+ // Non-transparent functor mentioned as a template parameter. FIXIT.
+ Finder->addMatcher(
+ loc(qualType(
+ unless(elaboratedType()),
+ hasDeclaration(classTemplateSpecializationDecl(
+ unless(hasAnyTemplateArgument(templateArgument(refersToType(
+ qualType(pointsTo(qualType(isAnyCharacter()))))))),
+ hasAnyTemplateArgument(
+ templateArgument(refersToType(qualType(hasDeclaration(
+ TransparentFunctors))))
+ .bind("Functor"))))))
+ .bind("FunctorParentLoc"),
+ this);
+
+ if (SafeMode)
+ return;
+
+ // Non-transparent functor constructed. No FIXIT. There is no easy way
+ // to rule out the problematic char* vs string case.
+ Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl(
+ ofClass(TransparentFunctors))),
+ unless(isInTemplateInstantiation()))
+ .bind("FuncInst"),
+ this);
+}
+
+static const StringRef Message = "prefer transparent functors '%0'";
+
+template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) {
+ T Result;
+ while (Result.isNull() && !Loc.isNull()) {
+ Result = Loc.getAs<T>();
+ Loc = Loc.getNextTypeLoc();
+ }
+ return Result;
+}
+
+void UseTransparentFunctorsCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *FuncClass =
+ Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass");
+ if (const auto *FuncInst =
+ Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) {
+ diag(FuncInst->getLocStart(), Message)
+ << (FuncClass->getName() + "<>").str();
+ return;
+ }
+
+ const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor");
+ const auto FunctorParentLoc =
+ Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc")
+ ->getAs<TemplateSpecializationTypeLoc>();
+
+ if (!FunctorParentLoc)
+ return;
+
+ unsigned ArgNum = 0;
+ const auto *FunctorParentType =
+ FunctorParentLoc.getType()->castAs<TemplateSpecializationType>();
+ for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) {
+ const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum);
+ if (Arg.getKind() != TemplateArgument::Type)
+ continue;
+ QualType ParentArgType = Arg.getAsType();
+ if (ParentArgType->isRecordType() &&
+ ParentArgType->getAsCXXRecordDecl() ==
+ Functor->getAsType()->getAsCXXRecordDecl())
+ break;
+ }
+ // Functor is a default template argument.
+ if (ArgNum == FunctorParentType->getNumArgs())
+ return;
+ TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum);
+ auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>(
+ FunctorLoc.getTypeSourceInfo()->getTypeLoc());
+ if (FunctorTypeLoc.isNull())
+ return;
+
+ SourceLocation ReportLoc = FunctorLoc.getLocation();
+ diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str()
+ << FixItHint::CreateRemoval(
+ FunctorTypeLoc.getArgLoc(0).getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseTransparentFunctorsCheck.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_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Prefer using transparent functors to non-transparent ones.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-transparent-functors.html
+class UseTransparentFunctorsCheck : public ClangTidyCheck {
+public:
+ UseTransparentFunctorsCheck(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 SafeMode;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
--- /dev/null
+//===--- UseUsingCheck.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 "UseUsingCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseUsingCheck::UseUsingCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus11)
+ return;
+ Finder->addMatcher(typedefDecl().bind("typedef"), this);
+}
+
+// Checks if 'typedef' keyword can be removed - we do it only if
+// it is the only declaration in a declaration chain.
+static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
+ ASTContext &Context) {
+ assert(StartLoc.isFileID() && "StartLoc must not be in a macro");
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc);
+ StringRef File = SM.getBufferData(LocInfo.first);
+ const char *TokenBegin = File.data() + LocInfo.second;
+ Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
+ File.begin(), TokenBegin, File.end());
+
+ Token Tok;
+ int ParenLevel = 0;
+ bool FoundTypedef = false;
+
+ while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
+ switch (Tok.getKind()) {
+ case tok::l_brace:
+ case tok::r_brace:
+ // This might be the `typedef struct {...} T;` case.
+ return false;
+ case tok::l_paren:
+ ParenLevel++;
+ break;
+ case tok::r_paren:
+ ParenLevel--;
+ break;
+ case tok::comma:
+ if (ParenLevel == 0) {
+ // If there is comma and we are not between open parenthesis then it is
+ // two or more declarations in this chain.
+ return false;
+ }
+ break;
+ case tok::raw_identifier:
+ if (Tok.getRawIdentifier() == "typedef") {
+ FoundTypedef = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Sanity check against weird macro cases.
+ return FoundTypedef;
+}
+
+void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
+ if (MatchedDecl->getLocation().isInvalid())
+ return;
+
+ auto &Context = *Result.Context;
+ auto &SM = *Result.SourceManager;
+
+ SourceLocation StartLoc = MatchedDecl->getLocStart();
+
+ if (StartLoc.isMacroID() && IgnoreMacros)
+ return;
+
+ auto Diag =
+ diag(StartLoc, "use 'using' instead of 'typedef'");
+
+ // do not fix if there is macro or array
+ if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID())
+ return;
+
+ if (CheckRemoval(SM, StartLoc, Context)) {
+ auto printPolicy = PrintingPolicy(getLangOpts());
+ printPolicy.SuppressScope = true;
+ printPolicy.ConstantArraySizeAsWritten = true;
+ printPolicy.UseVoidForZeroParams = false;
+
+ Diag << FixItHint::CreateReplacement(
+ MatchedDecl->getSourceRange(),
+ "using " + MatchedDecl->getNameAsString() + " = " +
+ MatchedDecl->getUnderlyingType().getAsString(printPolicy));
+ }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UseUsingCheck.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_MODERNIZE_USE_USING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_USING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Check finds typedefs and replaces it with usings.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-using.html
+class UseUsingCheck : public ClangTidyCheck {
+
+ const bool IgnoreMacros;
+
+public:
+ UseUsingCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+ Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+ }
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_USING_H
--- /dev/null
+//===--- BufferDerefCheck.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 "BufferDerefCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(callExpr().bind("CE"), this);
+}
+
+void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
+ static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context);
+ const auto *CE = Result.Nodes.getNodeAs<CallExpr>("CE");
+ if (!CE->getDirectCallee())
+ return;
+
+ const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
+ if (!Identifier || !FuncClassifier.isMPIType(Identifier))
+ return;
+
+ // These containers are used, to capture the type and expression of a buffer.
+ SmallVector<const Type *, 1> BufferTypes;
+ SmallVector<const Expr *, 1> BufferExprs;
+
+ // Adds the type and expression of a buffer that is used in the MPI call
+ // expression to the captured containers.
+ auto addBuffer = [&CE, &Result, &BufferTypes,
+ &BufferExprs](const size_t BufferIdx) {
+ // 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;
+
+ const Expr *ArgExpr = CE->getArg(BufferIdx);
+ if (!ArgExpr)
+ return;
+ const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
+ if (!ArgType)
+ return;
+ BufferExprs.push_back(ArgExpr);
+ BufferTypes.push_back(ArgType);
+ };
+
+ // Collect buffer types and argument expressions for all buffers used in the
+ // MPI call expression. The number passed to the lambda corresponds to the
+ // argument index of the currently verified MPI function call.
+ if (FuncClassifier.isPointToPointType(Identifier)) {
+ addBuffer(0);
+ } else if (FuncClassifier.isCollectiveType(Identifier)) {
+ if (FuncClassifier.isReduceType(Identifier)) {
+ addBuffer(0);
+ addBuffer(1);
+ } else if (FuncClassifier.isScatterType(Identifier) ||
+ FuncClassifier.isGatherType(Identifier) ||
+ FuncClassifier.isAlltoallType(Identifier)) {
+ addBuffer(0);
+ addBuffer(3);
+ } else if (FuncClassifier.isBcastType(Identifier)) {
+ addBuffer(0);
+ }
+ }
+
+ checkBuffers(BufferTypes, BufferExprs);
+}
+
+void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes,
+ ArrayRef<const Expr *> BufferExprs) {
+ for (size_t i = 0; i < BufferTypes.size(); ++i) {
+ unsigned IndirectionCount = 0;
+ const Type *BufferType = BufferTypes[i];
+ llvm::SmallVector<IndirectionType, 1> Indirections;
+
+ // Capture the depth and types of indirections for the passed buffer.
+ while (true) {
+ if (BufferType->isPointerType()) {
+ BufferType = BufferType->getPointeeType().getTypePtr();
+ Indirections.push_back(IndirectionType::Pointer);
+ } else if (BufferType->isArrayType()) {
+ BufferType = BufferType->getArrayElementTypeNoTypeQual();
+ Indirections.push_back(IndirectionType::Array);
+ } else {
+ break;
+ }
+ ++IndirectionCount;
+ }
+
+ if (IndirectionCount > 1) {
+ // Referencing an array with '&' is valid, as this also points to the
+ // beginning of the array.
+ if (IndirectionCount == 2 &&
+ Indirections[0] == IndirectionType::Pointer &&
+ Indirections[1] == IndirectionType::Array)
+ return;
+
+ // Build the indirection description in reverse order of discovery.
+ std::string IndirectionDesc;
+ for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) {
+ if (!IndirectionDesc.empty())
+ IndirectionDesc += "->";
+ if (*It == IndirectionType::Pointer) {
+ IndirectionDesc += "pointer";
+ } else {
+ IndirectionDesc += "array";
+ }
+ }
+
+ const auto Loc = BufferExprs[i]->getSourceRange().getBegin();
+ diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc;
+ }
+ }
+}
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- BufferDerefCheck.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_BUFFER_DEREF_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+/// This check verifies if a buffer passed to an MPI (Message Passing Interface)
+/// function is sufficiently dereferenced. Buffers should be passed as a single
+/// pointer or array. As MPI function signatures specify void * for their buffer
+/// types, insufficiently dereferenced buffers can be passed, like for example
+/// as double pointers or multidimensional arrays, without a compiler warning
+/// emitted.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html
+class BufferDerefCheck : public ClangTidyCheck {
+public:
+ BufferDerefCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ /// Checks for all buffers in an MPI call if they are sufficiently
+ /// dereferenced.
+ ///
+ /// \param BufferTypes buffer types
+ /// \param BufferExprs buffer arguments as expressions
+ void checkBuffers(ArrayRef<const Type *> BufferTypes,
+ ArrayRef<const Expr *> BufferExprs);
+
+ enum class IndirectionType : unsigned char { Pointer, Array };
+};
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyMPIModule
+ BufferDerefCheck.cpp
+ MPITidyModule.cpp
+ TypeMismatchCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ clangTooling
+ clangStaticAnalyzerCheckers
+ )
--- /dev/null
+//===--- 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 "BufferDerefCheck.h"
+#include "TypeMismatchCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+class MPIModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<BufferDerefCheck>("mpi-buffer-deref");
+ CheckFactories.registerCheck<TypeMismatchCheck>("mpi-type-mismatch");
+ }
+};
+
+} // namespace mpi
+
+// Register the MPITidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<mpi::MPIModule>
+ 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
--- /dev/null
+//===--- 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 <map>
+#include <unordered_set>
+
+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<BuiltinType::Kind, std::string> &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<std::string> 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<BuiltinType::Kind, std::string> 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<BuiltinType::Kind, std::string> 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<BuiltinType>();
+
+ if (Builtin &&
+ !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) {
+ BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str();
+ return false;
+ }
+ return true;
+}
+
+/// Check if a complex<float/double/long double> 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<BuiltinType::Kind, std::string> 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<BuiltinType>();
+
+ 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<std::string> 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 auto *const CE = Result.Nodes.getNodeAs<CallExpr>("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<const Type *, 1> BufferTypes;
+ SmallVector<const Expr *, 1> BufferExprs;
+ SmallVector<StringRef, 1> 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, getLangOpts());
+}
+
+void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes,
+ ArrayRef<const Expr *> BufferExprs,
+ ArrayRef<StringRef> 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<TypedefType>()) {
+ Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]);
+ } else if (const auto *Complex = BT->getAs<ComplexType>()) {
+ Error =
+ !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO);
+ } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) {
+ Error = !isCXXComplexTypeMatching(Template, BufferTypeName,
+ MPIDatatypes[i], LO);
+ } else if (const auto *Builtin = BT->getAs<BuiltinType>()) {
+ 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
--- /dev/null
+//===--- 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<const Type *> BufferTypes,
+ ArrayRef<const Expr *> BufferExprs,
+ ArrayRef<StringRef> MPIDatatypes, const LangOptions &LO);
+};
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyPerformanceModule
+ FasterStringFindCheck.cpp
+ ForRangeCopyCheck.cpp
+ ImplicitCastInLoopCheck.cpp
+ InefficientStringConcatenationCheck.cpp
+ InefficientVectorOperationCheck.cpp
+ PerformanceTidyModule.cpp
+ TypePromotionInMathFnCheck.cpp
+ UnnecessaryCopyInitialization.cpp
+ UnnecessaryValueParamCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ )
--- /dev/null
+//===--- FasterStringFindCheck.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 "FasterStringFindCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) {
+ std::string Result;
+ {
+ llvm::raw_string_ostream OS(Result);
+ Literal->outputString(OS);
+ }
+ // Now replace the " with '.
+ auto pos = Result.find_first_of('"');
+ if (pos == Result.npos)
+ return llvm::None;
+ Result[pos] = '\'';
+ pos = Result.find_last_of('"');
+ if (pos == Result.npos)
+ return llvm::None;
+ Result[pos] = '\'';
+ return Result;
+}
+
+AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
+ hasSubstitutedType) {
+ return hasType(qualType(anyOf(substTemplateTypeParmType(),
+ hasDescendant(substTemplateTypeParmType()))));
+}
+
+} // namespace
+
+FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StringLikeClasses(utils::options::parseStringList(
+ Options.get("StringLikeClasses", "std::basic_string"))) {}
+
+void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StringLikeClasses",
+ utils::options::serializeStringList(StringLikeClasses));
+}
+
+void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto SingleChar =
+ expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
+ const auto StringFindFunctions =
+ hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
+ "find_last_of", "find_last_not_of");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ callee(functionDecl(StringFindFunctions).bind("func")),
+ anyOf(argumentCountIs(1), argumentCountIs(2)),
+ hasArgument(0, SingleChar),
+ on(expr(hasType(recordDecl(hasAnyName(SmallVector<StringRef, 4>(
+ StringLikeClasses.begin(), StringLikeClasses.end())))),
+ unless(hasSubstitutedType())))),
+ this);
+}
+
+void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
+ const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ auto Replacement = MakeCharacterLiteral(Literal);
+ if (!Replacement)
+ return;
+
+ diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
+ "a single character; consider using the more "
+ "effective overload accepting a character")
+ << FindFunc << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Literal->getLocStart(),
+ Literal->getLocEnd()),
+ *Replacement);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- FasterStringFindCheck.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_FASTER_STRING_FIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
+
+#include "../ClangTidy.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Optimize calls to std::string::find() and friends when the needle passed is
+/// a single character string literal.
+/// The character literal overload is more efficient.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html
+class FasterStringFindCheck : public ClangTidyCheck {
+public:
+ FasterStringFindCheck(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 std::vector<std::string> StringLikeClasses;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
--- /dev/null
+//===--- ForRangeCopyCheck.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 "ForRangeCopyCheck.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/TypeTraits.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
+
+void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
+}
+
+void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
+ // Match loop variables that are not references or pointers or are already
+ // initialized through MaterializeTemporaryExpr which indicates a type
+ // conversion.
+ auto LoopVar = varDecl(
+ hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
+ unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
+ Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
+ .bind("forRange"),
+ this);
+}
+
+void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
+ // Ignore code in macros since we can't place the fixes correctly.
+ if (Var->getLocStart().isMacroID())
+ return;
+ if (handleConstValueCopy(*Var, *Result.Context))
+ return;
+ const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
+ handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
+}
+
+bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
+ ASTContext &Context) {
+ if (WarnOnAllAutoCopies) {
+ // For aggressive check just test that loop variable has auto type.
+ if (!isa<AutoType>(LoopVar.getType()))
+ return false;
+ } else if (!LoopVar.getType().isConstQualified()) {
+ return false;
+ }
+ llvm::Optional<bool> Expensive =
+ utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
+ if (!Expensive || !*Expensive)
+ return false;
+ auto Diagnostic =
+ diag(LoopVar.getLocation(),
+ "the loop variable's type is not a reference type; this creates a "
+ "copy in each iteration; consider making this a reference")
+ << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+ if (!LoopVar.getType().isConstQualified())
+ Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
+ return true;
+}
+
+bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
+ const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
+ ASTContext &Context) {
+ llvm::Optional<bool> Expensive =
+ utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
+ if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
+ return false;
+ if (!utils::decl_ref_expr::isOnlyUsedAsConst(LoopVar, *ForRange.getBody(),
+ Context))
+ return false;
+ diag(LoopVar.getLocation(),
+ "loop variable is copied but only used as const reference; consider "
+ "making it a const reference")
+ << utils::fixit::changeVarDeclToConst(LoopVar)
+ << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+ return true;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ForRangeCopyCheck.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_FORRANGECOPYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// A check that detects copied loop variables and suggests using const
+/// references.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-for-range-copy.html
+class ForRangeCopyCheck : public ClangTidyCheck {
+public:
+ ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ // Checks if the loop variable is a const value and expensive to copy. If so
+ // suggests it be converted to a const reference.
+ bool handleConstValueCopy(const VarDecl &LoopVar, ASTContext &Context);
+
+ // Checks if the loop variable is a non-const value and whether only
+ // const methods are invoked on it or whether it is only used as a const
+ // reference argument. If so it suggests it be made a const reference.
+ bool handleCopyIsOnlyConstReferenced(const VarDecl &LoopVar,
+ const CXXForRangeStmt &ForRange,
+ ASTContext &Context);
+
+ const bool WarnOnAllAutoCopies;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
--- /dev/null
+//===--- ImplicitCastInLoopCheck.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 "ImplicitCastInLoopCheck.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+// Checks if the stmt is a ImplicitCastExpr with a CastKind that is not a NoOp.
+// The subtelty is that in some cases (user defined conversions), we can
+// get to ImplicitCastExpr inside each other, with the outer one a NoOp. In this
+// case we skip the first cast expr.
+bool IsNonTrivialImplicitCast(const Stmt *ST) {
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) {
+ return (ICE->getCastKind() != CK_NoOp) ||
+ IsNonTrivialImplicitCast(ICE->getSubExpr());
+ }
+ return false;
+}
+} // namespace
+
+void ImplicitCastInLoopCheck::registerMatchers(MatchFinder *Finder) {
+ // We look for const ref loop variables that (optionally inside an
+ // ExprWithCleanup) materialize a temporary, and contain a implicit cast. The
+ // check on the implicit cast is done in check() because we can't access
+ // implicit cast subnode via matchers: has() skips casts and materialize!
+ // We also bind on the call to operator* to get the proper type in the
+ // diagnostic message.
+ // Note that when the implicit cast is done through a user defined cast
+ // operator, the node is a CXXMemberCallExpr, not a CXXOperatorCallExpr, so
+ // it should not get caught by the cxxOperatorCallExpr() matcher.
+ Finder->addMatcher(
+ cxxForRangeStmt(hasLoopVariable(
+ varDecl(hasType(qualType(references(qualType(isConstQualified())))),
+ hasInitializer(expr(hasDescendant(cxxOperatorCallExpr().bind(
+ "operator-call")))
+ .bind("init")))
+ .bind("faulty-var"))),
+ this);
+}
+
+void ImplicitCastInLoopCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *VD = Result.Nodes.getNodeAs<VarDecl>("faulty-var");
+ const auto *Init = Result.Nodes.getNodeAs<Expr>("init");
+ const auto *OperatorCall =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("operator-call");
+
+ if (const auto *Cleanup = dyn_cast<ExprWithCleanups>(Init))
+ Init = Cleanup->getSubExpr();
+
+ const auto *Materialized = dyn_cast<MaterializeTemporaryExpr>(Init);
+ if (!Materialized)
+ return;
+
+ // We ignore NoOp casts. Those are generated if the * operator on the
+ // iterator returns a value instead of a reference, and the loop variable
+ // is a reference. This situation is fine (it probably produces the same
+ // code at the end).
+ if (IsNonTrivialImplicitCast(Materialized->getTemporary()))
+ ReportAndFix(Result.Context, VD, OperatorCall);
+}
+
+void ImplicitCastInLoopCheck::ReportAndFix(
+ const ASTContext *Context, const VarDecl *VD,
+ const CXXOperatorCallExpr *OperatorCall) {
+ // We only match on const ref, so we should print a const ref version of the
+ // type.
+ QualType ConstType = OperatorCall->getType().withConst();
+ QualType ConstRefType = Context->getLValueReferenceType(ConstType);
+ const char Message[] =
+ "the type of the loop variable %0 is different from the one returned "
+ "by the iterator and generates an implicit cast; you can either "
+ "change the type to the correct one (%1 but 'const auto&' is always a "
+ "valid option) or remove the reference to make it explicit that you are "
+ "creating a new value";
+ diag(VD->getLocStart(), Message) << VD << ConstRefType;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ImplicitCastInLoopCheck.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_IMPLICIT_CAST_IN_LOOP_CHECK_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+// Checks that in a for range loop, if the provided type is a reference, then
+// the underlying type is the one returned by the iterator (i.e. that there
+// isn't any implicit conversion).
+class ImplicitCastInLoopCheck : public ClangTidyCheck {
+public:
+ ImplicitCastInLoopCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void ReportAndFix(const ASTContext *Context, const VarDecl *VD,
+ const CXXOperatorCallExpr *OperatorCall);
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
--- /dev/null
+//===--- 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.getLocalOrGlobal("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<DeclRefExpr>("lhsStr");
+ const auto *PlusOperator =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>("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
--- /dev/null
+//===--- 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
--- /dev/null
+//===--- InefficientVectorOperationCheck.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 "InefficientVectorOperationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/OptionsUtils.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+// Matcher names. Given the code:
+//
+// \code
+// void f() {
+// vector<T> v;
+// for (int i = 0; i < 10 + 1; ++i) {
+// v.push_back(i);
+// }
+// }
+// \endcode
+//
+// The matcher names are bound to following parts of the AST:
+// - LoopCounterName: The entire for loop (as ForStmt).
+// - LoopParentName: The body of function f (as CompoundStmt).
+// - VectorVarDeclName: 'v' in (as VarDecl).
+// - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
+// DeclStmt).
+// - PushBackOrEmplaceBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr).
+// - LoopInitVarName: 'i' (as VarDecl).
+// - LoopEndExpr: '10+1' (as Expr).
+static const char LoopCounterName[] = "for_loop_counter";
+static const char LoopParentName[] = "loop_parent";
+static const char VectorVarDeclName[] = "vector_var_decl";
+static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
+static const char PushBackOrEmplaceBackCallName[] = "append_call";
+static const char LoopInitVarName[] = "loop_init_var";
+static const char LoopEndExprName[] = "loop_end_expr";
+
+static const char RangeLoopName[] = "for_range_loop";
+
+ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
+ return hasType(cxxRecordDecl(hasAnyName(
+ "::std::vector", "::std::set", "::std::unordered_set", "::std::map",
+ "::std::unordered_map", "::std::array", "::std::deque")));
+}
+
+} // namespace
+
+InefficientVectorOperationCheck::InefficientVectorOperationCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ VectorLikeClasses(utils::options::parseStringList(
+ Options.get("VectorLikeClasses", "::std::vector"))) {}
+
+void InefficientVectorOperationCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "VectorLikeClasses",
+ utils::options::serializeStringList(VectorLikeClasses));
+}
+
+void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
+ const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
+ VectorLikeClasses.begin(), VectorLikeClasses.end())));
+ const auto VectorDefaultConstructorCall = cxxConstructExpr(
+ hasType(VectorDecl),
+ hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
+ const auto VectorVarDecl =
+ varDecl(hasInitializer(VectorDefaultConstructorCall))
+ .bind(VectorVarDeclName);
+ const auto VectorAppendCallExpr =
+ cxxMemberCallExpr(
+ callee(cxxMethodDecl(hasAnyName("push_back", "emplace_back"))),
+ on(hasType(VectorDecl)),
+ onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
+ .bind(PushBackOrEmplaceBackCallName);
+ const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr));
+ const auto VectorVarDefStmt =
+ declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
+ .bind(VectorVarDeclStmtName);
+
+ const auto LoopVarInit =
+ declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
+ .bind(LoopInitVarName)));
+ const auto RefersToLoopVar = ignoringParenImpCasts(
+ declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
+
+ // Matchers for the loop whose body has only 1 push_back/emplace_back calling
+ // statement.
+ const auto HasInterestingLoopBody =
+ hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)),
+ VectorAppendCall));
+ const auto InInterestingCompoundStmt =
+ hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
+
+ // Match counter-based for loops:
+ // for (int i = 0; i < n; ++i) { v.push_back(...); }
+ //
+ // FIXME: Support more types of counter-based loops like decrement loops.
+ Finder->addMatcher(
+ forStmt(
+ hasLoopInit(LoopVarInit),
+ hasCondition(binaryOperator(
+ hasOperatorName("<"), hasLHS(RefersToLoopVar),
+ hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
+ .bind(LoopEndExprName)))),
+ hasIncrement(unaryOperator(hasOperatorName("++"),
+ hasUnaryOperand(RefersToLoopVar))),
+ HasInterestingLoopBody, InInterestingCompoundStmt)
+ .bind(LoopCounterName),
+ this);
+
+ // Match for-range loops:
+ // for (const auto& E : data) { v.push_back(...); }
+ //
+ // FIXME: Support more complex range-expressions.
+ Finder->addMatcher(
+ cxxForRangeStmt(
+ hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
+ HasInterestingLoopBody, InInterestingCompoundStmt)
+ .bind(RangeLoopName),
+ this);
+}
+
+void InefficientVectorOperationCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ auto* Context = Result.Context;
+ if (Context->getDiagnostics().hasUncompilableErrorOccurred())
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+ const auto *VectorVarDecl =
+ Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
+ const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
+ const auto *RangeLoop =
+ Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
+ const auto *VectorAppendCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
+ const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
+ const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
+
+ const Stmt *LoopStmt = ForLoop;
+ if (!LoopStmt)
+ LoopStmt = RangeLoop;
+
+ llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
+ utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
+ *Context);
+ for (const auto *Ref : AllVectorVarRefs) {
+ // Skip cases where there are usages (defined as DeclRefExpr that refers to
+ // "v") of vector variable `v` before the for loop. We consider these usages
+ // are operations causing memory preallocation (e.g. "v.resize(n)",
+ // "v.reserve(n)").
+ //
+ // FIXME: make it more intelligent to identify the pre-allocating operations
+ // before the for loop.
+ if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
+ LoopStmt->getLocStart())) {
+ return;
+ }
+ }
+
+ llvm::StringRef VectorVarName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ VectorAppendCall->getImplicitObjectArgument()->getSourceRange()),
+ SM, Context->getLangOpts());
+
+ std::string ReserveStmt;
+ // Handle for-range loop cases.
+ if (RangeLoop) {
+ // Get the range-expression in a for-range statement represented as
+ // `for (range-declarator: range-expression)`.
+ StringRef RangeInitExpName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(
+ RangeLoop->getRangeInit()->getSourceRange()),
+ SM, Context->getLangOpts());
+
+ ReserveStmt =
+ (VectorVarName + ".reserve(" + RangeInitExpName + ".size()" + ");\n")
+ .str();
+ } else if (ForLoop) {
+ // Handle counter-based loop cases.
+ StringRef LoopEndSource = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
+ Context->getLangOpts());
+ ReserveStmt = (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
+ }
+
+ auto Diag =
+ diag(VectorAppendCall->getLocStart(),
+ "%0 is called inside a loop; "
+ "consider pre-allocating the vector capacity before the loop")
+ << VectorAppendCall->getMethodDecl()->getDeclName();
+
+ if (!ReserveStmt.empty())
+ Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- InefficientVectorOperationCheck.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_INEFFICIENT_VECTOR_OPERATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Finds possible inefficient `std::vector` operations (e.g. `push_back`) in
+/// for loops that may cause unnecessary memory reallocations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html
+class InefficientVectorOperationCheck : public ClangTidyCheck {
+public:
+ InefficientVectorOperationCheck(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 std::vector<std::string> VectorLikeClasses;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
--- /dev/null
+//===--- PeformanceTidyModule.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 "FasterStringFindCheck.h"
+#include "ForRangeCopyCheck.h"
+#include "ImplicitCastInLoopCheck.h"
+#include "InefficientStringConcatenationCheck.h"
+#include "InefficientVectorOperationCheck.h"
+#include "TypePromotionInMathFnCheck.h"
+#include "UnnecessaryCopyInitialization.h"
+#include "UnnecessaryValueParamCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+class PerformanceModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<FasterStringFindCheck>(
+ "performance-faster-string-find");
+ CheckFactories.registerCheck<ForRangeCopyCheck>(
+ "performance-for-range-copy");
+ CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
+ "performance-implicit-cast-in-loop");
+ CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
+ "performance-inefficient-string-concatenation");
+ CheckFactories.registerCheck<InefficientVectorOperationCheck>(
+ "performance-inefficient-vector-operation");
+ CheckFactories.registerCheck<TypePromotionInMathFnCheck>(
+ "performance-type-promotion-in-math-fn");
+ CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
+ "performance-unnecessary-copy-initialization");
+ CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
+ "performance-unnecessary-value-param");
+ }
+};
+
+// Register the PerformanceModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<PerformanceModule>
+ X("performance-module", "Adds performance checks.");
+
+} // namespace performance
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the PerformanceModule.
+volatile int PerformanceModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TypePromotionInMathFnCheck.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 "TypePromotionInMathFnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+AST_MATCHER_P(Type, isBuiltinType, BuiltinType::Kind, Kind) {
+ if (const auto *BT = dyn_cast<BuiltinType>(&Node)) {
+ return BT->getKind() == Kind;
+ }
+ return false;
+}
+} // anonymous namespace
+
+TypePromotionInMathFnCheck::TypePromotionInMathFnCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void TypePromotionInMathFnCheck::registerPPCallbacks(
+ CompilerInstance &Compiler) {
+ IncludeInserter = llvm::make_unique<utils::IncludeInserter>(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
+ Compiler.getPreprocessor().addPPCallbacks(
+ IncludeInserter->CreatePPCallbacks());
+}
+
+void TypePromotionInMathFnCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void TypePromotionInMathFnCheck::registerMatchers(MatchFinder *Finder) {
+ constexpr BuiltinType::Kind IntTy = BuiltinType::Int;
+ constexpr BuiltinType::Kind LongTy = BuiltinType::Long;
+ constexpr BuiltinType::Kind FloatTy = BuiltinType::Float;
+ constexpr BuiltinType::Kind DoubleTy = BuiltinType::Double;
+ constexpr BuiltinType::Kind LongDoubleTy = BuiltinType::LongDouble;
+
+ auto hasBuiltinTyParam = [](int Pos, BuiltinType::Kind Kind) {
+ return hasParameter(Pos, hasType(isBuiltinType(Kind)));
+ };
+ auto hasBuiltinTyArg = [](int Pos, BuiltinType::Kind Kind) {
+ return hasArgument(Pos, hasType(isBuiltinType(Kind)));
+ };
+
+ // Match calls to foo(double) with a float argument.
+ auto OneDoubleArgFns = hasAnyName(
+ "::acos", "::acosh", "::asin", "::asinh", "::atan", "::atanh", "::cbrt",
+ "::ceil", "::cos", "::cosh", "::erf", "::erfc", "::exp", "::exp2",
+ "::expm1", "::fabs", "::floor", "::ilogb", "::lgamma", "::llrint",
+ "::log", "::log10", "::log1p", "::log2", "::logb", "::lrint", "::modf",
+ "::nearbyint", "::rint", "::round", "::sin", "::sinh", "::sqrt", "::tan",
+ "::tanh", "::tgamma", "::trunc", "::llround", "::lround");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(OneDoubleArgFns, parameterCountIs(1),
+ hasBuiltinTyParam(0, DoubleTy))),
+ hasBuiltinTyArg(0, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to foo(double, double) where both args are floats.
+ auto TwoDoubleArgFns = hasAnyName("::atan2", "::copysign", "::fdim", "::fmax",
+ "::fmin", "::fmod", "::hypot", "::ldexp",
+ "::nextafter", "::pow", "::remainder");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(TwoDoubleArgFns, parameterCountIs(2),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasBuiltinTyParam(1, DoubleTy))),
+ hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to fma(double, double, double) where all args are floats.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::fma"), parameterCountIs(3),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasBuiltinTyParam(1, DoubleTy),
+ hasBuiltinTyParam(2, DoubleTy))),
+ hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy),
+ hasBuiltinTyArg(2, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to frexp(double, int*) where the first arg is a float.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(
+ hasName("::frexp"), parameterCountIs(2),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasParameter(1, parmVarDecl(hasType(pointerType(
+ pointee(isBuiltinType(IntTy)))))))),
+ hasBuiltinTyArg(0, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to nexttoward(double, long double) where the first arg is a
+ // float.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasBuiltinTyParam(1, LongDoubleTy))),
+ hasBuiltinTyArg(0, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to remquo(double, double, int*) where the first two args are
+ // floats.
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(
+ hasName("::remquo"), parameterCountIs(3),
+ hasBuiltinTyParam(0, DoubleTy), hasBuiltinTyParam(1, DoubleTy),
+ hasParameter(2, parmVarDecl(hasType(pointerType(
+ pointee(isBuiltinType(IntTy)))))))),
+ hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to scalbln(double, long) where the first arg is a float.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasBuiltinTyParam(1, LongTy))),
+ hasBuiltinTyArg(0, FloatTy))
+ .bind("call"),
+ this);
+
+ // Match calls to scalbn(double, int) where the first arg is a float.
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
+ hasBuiltinTyParam(0, DoubleTy),
+ hasBuiltinTyParam(1, IntTy))),
+ hasBuiltinTyArg(0, FloatTy))
+ .bind("call"),
+ this);
+
+ // modf(double, double*) is omitted because the second parameter forces the
+ // type -- there's no conversion from float* to double*.
+}
+
+void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ assert(Call != nullptr);
+
+ StringRef OldFnName = Call->getDirectCallee()->getName();
+
+ // In C++ mode, we prefer std::foo to ::foof. But some of these suggestions
+ // are only valid in C++11 and newer.
+ static llvm::StringSet<> Cpp11OnlyFns = {
+ "acosh", "asinh", "atanh", "cbrt", "copysign", "erf",
+ "erfc", "exp2", "expm1", "fdim", "fma", "fmax",
+ "fmin", "hypot", "ilogb", "lgamma", "llrint", "llround",
+ "log1p", "log2", "logb", "lrint", "lround", "nearbyint",
+ "nextafter", "nexttoward", "remainder", "remquo", "rint", "round",
+ "scalbln", "scalbn", "tgamma", "trunc"};
+ bool StdFnRequiresCpp11 = Cpp11OnlyFns.count(OldFnName);
+
+ std::string NewFnName;
+ bool FnInCmath = false;
+ if (getLangOpts().CPlusPlus &&
+ (!StdFnRequiresCpp11 || getLangOpts().CPlusPlus11)) {
+ NewFnName = ("std::" + OldFnName).str();
+ FnInCmath = true;
+ } else {
+ NewFnName = (OldFnName + "f").str();
+ }
+
+ auto Diag = diag(Call->getExprLoc(), "call to '%0' promotes float to double")
+ << OldFnName
+ << FixItHint::CreateReplacement(
+ Call->getCallee()->getSourceRange(), NewFnName);
+
+ // Suggest including <cmath> if the function we're suggesting is declared in
+ // <cmath> and it's not already included. We never have to suggest including
+ // <math.h>, because the functions we're suggesting moving away from are all
+ // declared in <math.h>.
+ if (FnInCmath)
+ if (auto IncludeFixit = IncludeInserter->CreateIncludeInsertion(
+ Result.Context->getSourceManager().getFileID(Call->getLocStart()),
+ "cmath", /*IsAngled=*/true))
+ Diag << *IncludeFixit;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TypePromotionInMathFnCheck.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_TYPE_PROMOTION_IN_MATH_FN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Finds calls to C math library functions with implicit float to double
+/// promotions.
+///
+/// For example, warns on ::sin(0.f), because this funciton's parameter is a
+/// double. You probably meant to call std::sin(0.f) (in C++), or sinf(0.f) (in
+/// C).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-type-promotion-in-math-fn.html
+class TypePromotionInMathFnCheck : public ClangTidyCheck {
+public:
+ TypePromotionInMathFnCheck(StringRef Name, ClangTidyContext *Context);
+
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ std::unique_ptr<utils::IncludeInserter> IncludeInserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
--- /dev/null
+//===--- UnnecessaryCopyInitialization.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 "UnnecessaryCopyInitialization.h"
+
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+namespace {
+
+void recordFixes(const VarDecl &Var, ASTContext &Context,
+ DiagnosticBuilder &Diagnostic) {
+ Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
+ if (!Var.getType().isLocalConstQualified())
+ Diagnostic << utils::fixit::changeVarDeclToConst(Var);
+}
+
+} // namespace
+
+using namespace ::clang::ast_matchers;
+using utils::decl_ref_expr::isOnlyUsedAsConst;
+
+void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
+ auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
+ auto ConstOrConstReference =
+ allOf(anyOf(ConstReference, isConstQualified()),
+ unless(allOf(pointerType(), unless(pointerType(pointee(
+ qualType(isConstQualified())))))));
+
+ // Match method call expressions where the `this` argument is only used as
+ // const, this will be checked in `check()` part. This returned const
+ // reference is highly likely to outlive the local const reference of the
+ // variable being declared. The assumption is that the const reference being
+ // returned either points to a global static variable or to a member of the
+ // called object.
+ auto ConstRefReturningMethodCall =
+ cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
+ on(declRefExpr(to(varDecl().bind("objectArg")))));
+ auto ConstRefReturningFunctionCall =
+ callExpr(callee(functionDecl(returns(ConstReference))),
+ unless(callee(cxxMethodDecl())));
+
+ auto localVarCopiedFrom = [](const internal::Matcher<Expr> &CopyCtorArg) {
+ return compoundStmt(
+ forEachDescendant(
+ declStmt(
+ has(varDecl(hasLocalStorage(),
+ hasType(matchers::isExpensiveToCopy()),
+ unless(isImplicit()),
+ hasInitializer(
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(
+ isCopyConstructor())),
+ hasArgument(0, CopyCtorArg))
+ .bind("ctorCall")))
+ .bind("newVarDecl")))
+ .bind("declStmt")))
+ .bind("blockStmt");
+ };
+
+ Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
+ ConstRefReturningMethodCall)),
+ this);
+
+ Finder->addMatcher(localVarCopiedFrom(declRefExpr(
+ to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
+ this);
+}
+
+void UnnecessaryCopyInitialization::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
+ const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
+ const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
+ const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
+ const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
+ // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
+ // since we cannot place them correctly.
+ bool IssueFix =
+ Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
+ !NewVar->getLocation().isMacroID();
+
+ // A constructor that looks like T(const T& t, bool arg = false) counts as a
+ // copy only when it is called with default arguments for the arguments after
+ // the first.
+ for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
+ if (!CtorCall->getArg(i)->isDefaultArgument())
+ return;
+
+ if (OldVar == nullptr) {
+ handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
+ *Result.Context);
+ } else {
+ handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
+ *Result.Context);
+ }
+}
+
+void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
+ const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
+ const VarDecl *ObjectArg, ASTContext &Context) {
+ bool IsConstQualified = Var.getType().isConstQualified();
+ if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
+ return;
+ if (ObjectArg != nullptr &&
+ !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
+ return;
+
+ auto Diagnostic =
+ diag(Var.getLocation(),
+ IsConstQualified ? "the const qualified variable %0 is "
+ "copy-constructed from a const reference; "
+ "consider making it a const reference"
+ : "the variable %0 is copy-constructed from a "
+ "const reference but is only used as const "
+ "reference; consider making it a const reference")
+ << &Var;
+ if (IssueFix)
+ recordFixes(Var, Context, Diagnostic);
+}
+
+void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
+ const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
+ bool IssueFix, ASTContext &Context) {
+ if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
+ !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
+ return;
+
+ auto Diagnostic = diag(NewVar.getLocation(),
+ "local copy %0 of the variable %1 is never modified; "
+ "consider avoiding the copy")
+ << &NewVar << &OldVar;
+ if (IssueFix)
+ recordFixes(NewVar, Context, Diagnostic);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnnecessaryCopyInitialization.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_UNNECESSARY_COPY_INITIALIZATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+// The check detects local variable declarations that are copy initialized with
+// the const reference of a function call or the const reference of a method
+// call whose object is guaranteed to outlive the variable's scope and suggests
+// to use a const reference.
+//
+// The check currently only understands a subset of variables that are
+// guaranteed to outlive the const reference returned, namely: const variables,
+// const references, and const pointers to const.
+class UnnecessaryCopyInitialization : public ClangTidyCheck {
+public:
+ UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void handleCopyFromMethodReturn(const VarDecl &Var, const Stmt &BlockStmt,
+ bool IssueFix, const VarDecl *ObjectArg,
+ ASTContext &Context);
+ void handleCopyFromLocalVar(const VarDecl &NewVar, const VarDecl &OldVar,
+ const Stmt &BlockStmt, bool IssueFix,
+ ASTContext &Context);
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
--- /dev/null
+//===--- UnnecessaryValueParamCheck.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 "UnnecessaryValueParamCheck.h"
+
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+std::string paramNameOrIndex(StringRef Name, size_t Index) {
+ return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
+ : llvm::Twine('\'') + Name + llvm::Twine('\''))
+ .str();
+}
+
+template <typename S>
+bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
+ for (const auto &E : SubsetCandidate)
+ if (SupersetCandidate.count(E) == 0)
+ return false;
+ return true;
+}
+
+bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
+ ASTContext &Context) {
+ auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
+ unless(hasAncestor(callExpr()))),
+ Context);
+ return !Matches.empty();
+}
+
+bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context) {
+ auto Matches =
+ match(decl(forEachDescendant(declRefExpr(
+ equalsNode(&DeclRef),
+ unless(hasAncestor(stmt(anyOf(forStmt(), cxxForRangeStmt(),
+ whileStmt(), doStmt()))))))),
+ Decl, Context);
+ return Matches.empty();
+}
+
+} // namespace
+
+UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+ Options.get("IncludeStyle", "llvm"))) {}
+
+void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ExpensiveValueParamDecl =
+ parmVarDecl(hasType(hasCanonicalType(allOf(
+ unless(referenceType()), matchers::isExpensiveToCopy()))),
+ decl().bind("param"));
+ Finder->addMatcher(
+ functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()),
+ unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),
+ has(typeLoc(forEach(ExpensiveValueParamDecl))),
+ unless(isInstantiated()), decl().bind("functionDecl")),
+ this);
+}
+
+void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+ const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
+ const size_t Index = std::find(Function->parameters().begin(),
+ Function->parameters().end(), Param) -
+ Function->parameters().begin();
+ bool IsConstQualified =
+ Param->getType().getCanonicalType().isConstQualified();
+
+ auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+ *Param, *Function, *Result.Context);
+ auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
+ *Param, *Function, *Result.Context);
+
+ // Do not trigger on non-const value parameters when they are not only used as
+ // const.
+ if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+ return;
+
+ // If the parameter is non-const, check if it has a move constructor and is
+ // only referenced once to copy-construct another object or whether it has a
+ // move assignment operator and is only referenced once when copy-assigned.
+ // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
+ // copy.
+ if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
+ auto CanonicalType = Param->getType().getCanonicalType();
+ const auto &DeclRefExpr = **AllDeclRefExprs.begin();
+
+ if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+ ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+ utils::decl_ref_expr::isCopyConstructorArgument(
+ DeclRefExpr, *Function, *Result.Context)) ||
+ (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+ utils::decl_ref_expr::isCopyAssignmentArgument(
+ DeclRefExpr, *Function, *Result.Context)))) {
+ handleMoveFix(*Param, DeclRefExpr, *Result.Context);
+ return;
+ }
+ }
+
+ auto Diag =
+ diag(Param->getLocation(),
+ IsConstQualified ? "the const qualified parameter %0 is "
+ "copied for each invocation; consider "
+ "making it a reference"
+ : "the parameter %0 is copied for each "
+ "invocation but only used as a const reference; "
+ "consider making it a const reference")
+ << paramNameOrIndex(Param->getName(), Index);
+ // Do not propose fixes when:
+ // 1. the ParmVarDecl is in a macro, since we cannot place them correctly
+ // 2. the function is virtual as it might break overrides
+ // 3. the function is referenced outside of a call expression within the
+ // compilation unit as the signature change could introduce build errors.
+ const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
+ if (Param->getLocStart().isMacroID() || (Method && Method->isVirtual()) ||
+ isReferencedOutsideOfCallExpr(*Function, *Result.Context))
+ return;
+ for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
+ FunctionDecl = FunctionDecl->getPreviousDecl()) {
+ const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
+ Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
+ *Result.Context);
+ // The parameter of each declaration needs to be checked individually as to
+ // whether it is const or not as constness can differ between definition and
+ // declaration.
+ if (!CurrentParam.getType().getCanonicalType().isConstQualified())
+ Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
+ }
+}
+
+void UnnecessaryValueParamCheck::registerPPCallbacks(
+ CompilerInstance &Compiler) {
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void UnnecessaryValueParamCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IncludeStyle",
+ utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
+ const DeclRefExpr &CopyArgument,
+ const ASTContext &Context) {
+ auto Diag = diag(CopyArgument.getLocStart(),
+ "parameter %0 is passed by value and only copied once; "
+ "consider moving it to avoid unnecessary copies")
+ << &Var;
+ // Do not propose fixes in macros since we cannot place them correctly.
+ if (CopyArgument.getLocStart().isMacroID())
+ return;
+ const auto &SM = Context.getSourceManager();
+ 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(
+ SM.getFileID(CopyArgument.getLocStart()), "utility",
+ /*IsAngled=*/true))
+ Diag << *IncludeFixit;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UnnecessaryValueParamCheck.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_UNNECESSARY_VALUE_PARAM_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_VALUE_PARAM_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// \brief A check that flags value parameters of expensive to copy types that
+/// can safely be converted to const references.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html
+class UnnecessaryValueParamCheck : public ClangTidyCheck {
+public:
+ UnnecessaryValueParamCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
+ const ASTContext &Context);
+
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+ const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_VALUE_PARAM_H
--- /dev/null
+add_clang_library(clangTidyPlugin
+ ClangTidyPlugin.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangSema
+ clangTidy
+ clangTidyAndroidModule
+ clangTidyBoostModule
+ clangTidyCERTModule
+ clangTidyCppCoreGuidelinesModule
+ clangTidyGoogleModule
+ clangTidyLLVMModule
+ clangTidyMiscModule
+ clangTidyModernizeModule
+ clangTidyMPIModule
+ clangTidyPerformanceModule
+ clangTidyReadabilityModule
+ clangTooling
+ )
--- /dev/null
+//===- ClangTidyPlugin.cpp - clang-tidy as a clang plugin -----------------===//
+//
+// 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 "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+
+namespace clang {
+namespace tidy {
+
+/// The core clang tidy plugin action. This just provides the AST consumer and
+/// command line flag parsing for using clang-tidy as a clang plugin.
+class ClangTidyPluginAction : public PluginASTAction {
+ /// Wrapper to grant the context the same lifetime as the action. We use
+ /// MultiplexConsumer to avoid writing out all the forwarding methods.
+ class WrapConsumer : public MultiplexConsumer {
+ std::unique_ptr<ClangTidyContext> Context;
+
+ public:
+ WrapConsumer(std::unique_ptr<ClangTidyContext> Context,
+ std::vector<std::unique_ptr<ASTConsumer>> Consumer)
+ : MultiplexConsumer(std::move(Consumer)), Context(std::move(Context)) {}
+ };
+
+public:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef File) override {
+ // Insert the current diagnostics engine.
+ Context->setDiagnosticsEngine(&Compiler.getDiagnostics());
+
+ // Create the AST consumer.
+ ClangTidyASTConsumerFactory Factory(*Context);
+ std::vector<std::unique_ptr<ASTConsumer>> Vec;
+ Vec.push_back(Factory.CreateASTConsumer(Compiler, File));
+
+ return llvm::make_unique<WrapConsumer>(std::move(Context), std::move(Vec));
+ }
+
+ bool ParseArgs(const CompilerInstance &,
+ const std::vector<std::string> &Args) override {
+ ClangTidyGlobalOptions GlobalOptions;
+ ClangTidyOptions DefaultOptions;
+ ClangTidyOptions OverrideOptions;
+
+ // Parse the extra command line args.
+ // FIXME: This is very limited at the moment.
+ for (StringRef Arg : Args)
+ if (Arg.startswith("-checks="))
+ OverrideOptions.Checks = Arg.substr(strlen("-checks="));
+
+ auto Options = llvm::make_unique<FileOptionsProvider>(
+ GlobalOptions, DefaultOptions, OverrideOptions);
+ Context = llvm::make_unique<ClangTidyContext>(std::move(Options));
+ return true;
+ }
+
+private:
+ std::unique_ptr<ClangTidyContext> Context;
+};
+} // namespace tidy
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the clang-tidy plugin.
+volatile int ClangTidyPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<clang::tidy::ClangTidyPluginAction>
+ X("clang-tidy", "clang-tidy");
+
+namespace clang {
+namespace tidy {
+
+// This anchor is used to force the linker to link the CERTModule.
+extern volatile int CERTModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CERTModuleAnchorDestination =
+ CERTModuleAnchorSource;
+
+// This anchor is used to force the linker to link the LLVMModule.
+extern volatile int LLVMModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED LLVMModuleAnchorDestination =
+ LLVMModuleAnchorSource;
+
+// This anchor is used to force the linker to link the CppCoreGuidelinesModule.
+extern volatile int CppCoreGuidelinesModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
+ CppCoreGuidelinesModuleAnchorSource;
+
+// This anchor is used to force the linker to link the GoogleModule.
+extern volatile int GoogleModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination =
+ GoogleModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MiscModule.
+extern volatile int MiscModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MiscModuleAnchorDestination =
+ MiscModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ModernizeModule.
+extern volatile int ModernizeModuleAnchorSource;
+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 =
+ PerformanceModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ReadabilityModule.
+extern volatile int ReadabilityModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ReadabilityModuleAnchorDestination =
+ ReadabilityModuleAnchorSource;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- AvoidConstParamsInDecls.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 "AvoidConstParamsInDecls.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+SourceRange getTypeRange(const ParmVarDecl &Param) {
+ if (Param.getIdentifier() != nullptr)
+ return SourceRange(Param.getLocStart(),
+ Param.getLocEnd().getLocWithOffset(-1));
+ return Param.getSourceRange();
+}
+
+} // namespace
+
+void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) {
+ const auto ConstParamDecl =
+ parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
+ Finder->addMatcher(
+ functionDecl(unless(isDefinition()),
+ // Lambdas are always their own definition, but they
+ // generate a non-definition FunctionDecl too. Ignore those.
+ // Class template instantiations have a non-definition
+ // CXXMethodDecl for methods that aren't used in this
+ // translation unit. Ignore those, as the template will have
+ // already been checked.
+ unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf(
+ isLambda(), ast_matchers::isTemplateInstantiation()))))),
+ has(typeLoc(forEach(ConstParamDecl))))
+ .bind("func"),
+ this);
+}
+
+// Re-lex the tokens to get precise location of last 'const'
+static llvm::Optional<Token> ConstTok(CharSourceRange Range,
+ const MatchFinder::MatchResult &Result) {
+ const SourceManager &Sources = *Result.SourceManager;
+ std::pair<FileID, unsigned> LocInfo =
+ Sources.getDecomposedLoc(Range.getBegin());
+ StringRef File = Sources.getBufferData(LocInfo.first);
+ const char *TokenBegin = File.data() + LocInfo.second;
+ Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
+ Result.Context->getLangOpts(), File.begin(), TokenBegin,
+ File.end());
+ Token Tok;
+ llvm::Optional<Token> ConstTok;
+ while (!RawLexer.LexFromRawLexer(Tok)) {
+ if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
+ break;
+ if (Tok.is(tok::raw_identifier)) {
+ IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
+ Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
+ Tok.setIdentifierInfo(&Info);
+ Tok.setKind(Info.getTokenID());
+ }
+ if (Tok.is(tok::kw_const))
+ ConstTok = Tok;
+ }
+ return ConstTok;
+}
+
+void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+
+ if (!Param->getType().isLocalConstQualified())
+ return;
+
+ auto Diag = diag(Param->getLocStart(),
+ "parameter %0 is const-qualified in the function "
+ "declaration; const-qualification of parameters only has an "
+ "effect in function definitions");
+ if (Param->getName().empty()) {
+ for (unsigned int i = 0; i < Func->getNumParams(); ++i) {
+ if (Param == Func->getParamDecl(i)) {
+ Diag << (i + 1);
+ break;
+ }
+ }
+ } else {
+ Diag << Param;
+ }
+
+ if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) {
+ // Do not offer a suggestion if the part of the variable declaration comes
+ // from a macro.
+ return;
+ }
+
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(getTypeRange(*Param)),
+ *Result.SourceManager, getLangOpts());
+
+ if (!FileRange.isValid())
+ return;
+
+ auto Tok = ConstTok(FileRange, Result);
+ if (!Tok)
+ return;
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- AvoidConstParamsInDecls.h - clang-tidy----------------------------===//
+//
+// 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_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+// Detect function declarations that have const value parameters and discourage
+// them.
+class AvoidConstParamsInDecls : public ClangTidyCheck {
+public:
+ AvoidConstParamsInDecls(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
--- /dev/null
+//===--- BracesAroundStatementsCheck.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 "BracesAroundStatementsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+ const ASTContext *Context) {
+ Token Tok;
+ SourceLocation Beginning =
+ Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
+ const bool Invalid =
+ Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
+ assert(!Invalid && "Expected a valid token.");
+
+ if (Invalid)
+ return tok::NUM_TOKENS;
+
+ return Tok.getKind();
+}
+
+SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ assert(Loc.isValid());
+ for (;;) {
+ while (isWhitespace(*SM.getCharacterData(Loc)))
+ Loc = Loc.getLocWithOffset(1);
+
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
+ return Loc;
+
+ // Fast-forward current token.
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ }
+}
+
+SourceLocation findEndLocation(SourceLocation LastTokenLoc,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ SourceLocation Loc =
+ Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
+ // Loc points to the beginning of the last (non-comment non-ws) token
+ // before end or ';'.
+ assert(Loc.isValid());
+ bool SkipEndWhitespaceAndComments = true;
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
+ TokKind == tok::r_brace) {
+ // If we are at ";" or "}", we found the last token. We could use as well
+ // `if (isa<NullStmt>(S))`, but it wouldn't work for nested statements.
+ SkipEndWhitespaceAndComments = false;
+ }
+
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ // Loc points past the last token before end or after ';'.
+ if (SkipEndWhitespaceAndComments) {
+ Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind == tok::semi)
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ }
+
+ for (;;) {
+ assert(Loc.isValid());
+ while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
+ Loc = Loc.getLocWithOffset(1);
+ }
+
+ if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
+ // EOL, insert brace before.
+ break;
+ }
+ tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+ if (TokKind != tok::comment) {
+ // Non-comment token, insert brace before.
+ break;
+ }
+
+ SourceLocation TokEndLoc =
+ Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+ SourceRange TokRange(Loc, TokEndLoc);
+ StringRef Comment = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
+ if (Comment.startswith("/*") && Comment.find('\n') != StringRef::npos) {
+ // Multi-line block comment, insert brace before.
+ break;
+ }
+ // else: Trailing comment, insert brace after the newline.
+
+ // Fast-forward current token.
+ Loc = TokEndLoc;
+ }
+ return Loc;
+}
+
+} // namespace
+
+BracesAroundStatementsCheck::BracesAroundStatementsCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ // Always add braces by default.
+ ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}
+
+void BracesAroundStatementsCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ShortStatementLines", ShortStatementLines);
+}
+
+void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(ifStmt().bind("if"), this);
+ Finder->addMatcher(whileStmt().bind("while"), this);
+ Finder->addMatcher(doStmt().bind("do"), this);
+ Finder->addMatcher(forStmt().bind("for"), this);
+ Finder->addMatcher(cxxForRangeStmt().bind("for-range"), this);
+}
+
+void BracesAroundStatementsCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // Get location of closing parenthesis or 'do' to insert opening brace.
+ if (auto S = Result.Nodes.getNodeAs<ForStmt>("for")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {
+ checkStmt(Result, S->getBody(), S->getRParenLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<DoStmt>("do")) {
+ checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
+ } else if (auto S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ checkStmt(Result, S->getBody(), StartLoc);
+ } else if (auto S = Result.Nodes.getNodeAs<IfStmt>("if")) {
+ SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+ if (StartLoc.isInvalid())
+ return;
+ if (ForceBracesStmts.erase(S))
+ ForceBracesStmts.insert(S->getThen());
+ bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
+ const Stmt *Else = S->getElse();
+ if (Else && BracedIf)
+ ForceBracesStmts.insert(Else);
+ if (Else && !isa<IfStmt>(Else)) {
+ // Omit 'else if' statements here, they will be handled directly.
+ checkStmt(Result, Else, S->getElseLoc());
+ }
+ } else {
+ llvm_unreachable("Invalid match");
+ }
+}
+
+/// Find location of right parenthesis closing condition.
+template <typename IfOrWhileStmt>
+SourceLocation
+BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
+ const SourceManager &SM,
+ const ASTContext *Context) {
+ // Skip macros.
+ if (S->getLocStart().isMacroID())
+ return SourceLocation();
+
+ SourceLocation CondEndLoc = S->getCond()->getLocEnd();
+ if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
+ CondEndLoc = CondVar->getLocEnd();
+
+ if (!CondEndLoc.isValid()) {
+ return SourceLocation();
+ }
+
+ SourceLocation PastCondEndLoc =
+ Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
+ if (PastCondEndLoc.isInvalid())
+ return SourceLocation();
+ SourceLocation RParenLoc =
+ forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
+ if (RParenLoc.isInvalid())
+ return SourceLocation();
+ tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
+ if (TokKind != tok::r_paren)
+ return SourceLocation();
+ return RParenLoc;
+}
+
+/// Determine if the statement needs braces around it, and add them if it does.
+/// Returns true if braces where added.
+bool BracesAroundStatementsCheck::checkStmt(
+ const MatchFinder::MatchResult &Result, const Stmt *S,
+ SourceLocation InitialLoc, SourceLocation EndLocHint) {
+ // 1) If there's a corresponding "else" or "while", the check inserts "} "
+ // right before that token.
+ // 2) If there's a multi-line block comment starting on the same line after
+ // the location we're inserting the closing brace at, or there's a non-comment
+ // token, the check inserts "\n}" right before that token.
+ // 3) Otherwise the check finds the end of line (possibly after some block or
+ // line comments) and inserts "\n}" right before that EOL.
+ if (!S || isa<CompoundStmt>(S)) {
+ // Already inside braces.
+ return false;
+ }
+
+ if (!InitialLoc.isValid())
+ return false;
+ const SourceManager &SM = *Result.SourceManager;
+ const ASTContext *Context = Result.Context;
+
+ // Treat macros.
+ CharSourceRange FileRange = Lexer::makeFileCharRange(
+ CharSourceRange::getTokenRange(S->getSourceRange()), SM,
+ Context->getLangOpts());
+ if (FileRange.isInvalid())
+ return false;
+
+ // Convert InitialLoc to file location, if it's on the same macro expansion
+ // level as the start of the statement. We also need file locations for
+ // Lexer::getLocForEndOfToken working properly.
+ InitialLoc = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(InitialLoc, S->getLocStart()),
+ SM, Context->getLangOpts())
+ .getBegin();
+ if (InitialLoc.isInvalid())
+ return false;
+ SourceLocation StartLoc =
+ Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
+
+ // StartLoc points at the location of the opening brace to be inserted.
+ SourceLocation EndLoc;
+ std::string ClosingInsertion;
+ if (EndLocHint.isValid()) {
+ EndLoc = EndLocHint;
+ ClosingInsertion = "} ";
+ } else {
+ const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
+ EndLoc = findEndLocation(FREnd, SM, Context);
+ ClosingInsertion = "\n}";
+ }
+
+ assert(StartLoc.isValid());
+ assert(EndLoc.isValid());
+ // Don't require braces for statements spanning less than certain number of
+ // lines.
+ if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
+ unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
+ unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
+ if (EndLine - StartLine < ShortStatementLines)
+ return false;
+ }
+
+ auto Diag = diag(StartLoc, "statement should be inside braces");
+ Diag << FixItHint::CreateInsertion(StartLoc, " {")
+ << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
+ return true;
+}
+
+void BracesAroundStatementsCheck::onEndOfTranslationUnit() {
+ ForceBracesStmts.clear();
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- BracesAroundStatementsCheck.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_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks that bodies of `if` statements and loops (`for`, `range-for`,
+/// `do-while`, and `while`) are inside braces
+///
+/// Before:
+///
+/// \code
+/// if (condition)
+/// statement;
+/// \endcode
+///
+/// After:
+///
+/// \code
+/// if (condition) {
+/// statement;
+/// }
+/// \endcode
+///
+/// Additionally, one can define an option `ShortStatementLines` defining the
+/// minimal number of lines that the statement should have in order to trigger
+/// this check.
+///
+/// The number of lines is counted from the end of condition or initial keyword
+/// (`do`/`else`) until the last line of the inner statement. Default value 0
+/// means that braces will be added to all statements (not having them already).
+class BracesAroundStatementsCheck : public ClangTidyCheck {
+public:
+ BracesAroundStatementsCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void onEndOfTranslationUnit() override;
+
+private:
+ bool checkStmt(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Stmt *S, SourceLocation StartLoc,
+ SourceLocation EndLocHint = SourceLocation());
+ template <typename IfOrWhileStmt>
+ SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
+ const ASTContext *Context);
+
+private:
+ std::set<const Stmt *> ForceBracesStmts;
+ const unsigned ShortStatementLines;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyReadabilityModule
+ AvoidConstParamsInDecls.cpp
+ BracesAroundStatementsCheck.cpp
+ ContainerSizeEmptyCheck.cpp
+ DeleteNullPointerCheck.cpp
+ DeletedDefaultCheck.cpp
+ ElseAfterReturnCheck.cpp
+ FunctionSizeCheck.cpp
+ IdentifierNamingCheck.cpp
+ ImplicitBoolCastCheck.cpp
+ InconsistentDeclarationParameterNameCheck.cpp
+ MisleadingIndentationCheck.cpp
+ MisplacedArrayIndexCheck.cpp
+ NamedParameterCheck.cpp
+ NamespaceCommentCheck.cpp
+ NonConstParameterCheck.cpp
+ ReadabilityTidyModule.cpp
+ RedundantControlFlowCheck.cpp
+ RedundantDeclarationCheck.cpp
+ RedundantFunctionPtrDereferenceCheck.cpp
+ RedundantMemberInitCheck.cpp
+ RedundantStringCStrCheck.cpp
+ RedundantSmartptrGetCheck.cpp
+ RedundantStringInitCheck.cpp
+ SimplifyBooleanExprCheck.cpp
+ StaticDefinitionInAnonymousNamespaceCheck.cpp
+ UniqueptrDeleteReleaseCheck.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ clangTidyUtils
+ clangTooling
+ )
--- /dev/null
+//===--- ContainerSizeEmptyCheck.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 "ContainerSizeEmptyCheck.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+using utils::IsBinaryOrTernary;
+
+ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
+ namedDecl(
+ has(cxxMethodDecl(
+ isConst(), parameterCountIs(0), isPublic(), hasName("size"),
+ returns(qualType(isInteger(), unless(booleanType()))))
+ .bind("size")),
+ has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
+ hasName("empty"), returns(booleanType()))
+ .bind("empty")))
+ .bind("container")));
+
+ const auto WrongUse = anyOf(
+ hasParent(binaryOperator(
+ matchers::isComparisonOperator(),
+ hasEitherOperand(ignoringImpCasts(anyOf(
+ integerLiteral(equals(1)), integerLiteral(equals(0))))))
+ .bind("SizeBinaryOp")),
+ hasParent(implicitCastExpr(
+ hasImplicitDestinationType(booleanType()),
+ anyOf(
+ hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
+ anything()))),
+ hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
+ hasType(pointsTo(ValidContainer)),
+ hasType(references(ValidContainer))))),
+ callee(cxxMethodDecl(hasName("size"))), WrongUse,
+ unless(hasAncestor(cxxMethodDecl(
+ ofClass(equalsBoundNode("container"))))))
+ .bind("SizeCallExpr"),
+ this);
+
+ // Empty constructor matcher.
+ const auto DefaultConstructor = cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
+ // Comparison to empty string or empty constructor.
+ const auto WrongComparend = anyOf(
+ ignoringImpCasts(stringLiteral(hasSize(0))),
+ ignoringImpCasts(cxxBindTemporaryExpr(has(DefaultConstructor))),
+ ignoringImplicit(DefaultConstructor),
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+ has(expr(ignoringImpCasts(DefaultConstructor)))),
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(isMoveConstructor())),
+ has(expr(ignoringImpCasts(DefaultConstructor)))));
+ // Match the object being compared.
+ const auto STLArg =
+ anyOf(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(
+ expr(hasType(pointsTo(ValidContainer))).bind("Pointee"))),
+ expr(hasType(ValidContainer)).bind("STLObject"));
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ anyOf(hasOverloadedOperatorName("=="),
+ hasOverloadedOperatorName("!=")),
+ anyOf(allOf(hasArgument(0, WrongComparend), hasArgument(1, STLArg)),
+ allOf(hasArgument(0, STLArg), hasArgument(1, WrongComparend))),
+ unless(hasAncestor(
+ cxxMethodDecl(ofClass(equalsBoundNode("container"))))))
+ .bind("BinCmp"),
+ this);
+}
+
+void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MemberCall =
+ Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
+ const auto *BinCmp = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("BinCmp");
+ const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
+ const auto *Pointee = Result.Nodes.getNodeAs<Expr>("Pointee");
+ const auto *E =
+ MemberCall
+ ? MemberCall->getImplicitObjectArgument()
+ : (Pointee ? Pointee : Result.Nodes.getNodeAs<Expr>("STLObject"));
+ FixItHint Hint;
+ std::string ReplacementText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ if (BinCmp && IsBinaryOrTernary(E)) {
+ // Not just a DeclRefExpr, so parenthesize to be on the safe side.
+ ReplacementText = "(" + ReplacementText + ")";
+ }
+ if (E->getType()->isPointerType())
+ ReplacementText += "->empty()";
+ else
+ ReplacementText += ".empty()";
+
+ if (BinCmp) {
+ if (BinCmp->getOperator() == OO_ExclaimEqual) {
+ ReplacementText = "!" + ReplacementText;
+ }
+ Hint =
+ FixItHint::CreateReplacement(BinCmp->getSourceRange(), ReplacementText);
+ } else if (BinaryOp) { // Determine the correct transformation.
+ bool Negation = false;
+ const bool ContainerIsLHS =
+ !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
+ const auto OpCode = BinaryOp->getOpcode();
+ uint64_t Value = 0;
+ if (ContainerIsLHS) {
+ if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
+ BinaryOp->getRHS()->IgnoreImpCasts()))
+ Value = Literal->getValue().getLimitedValue();
+ else
+ return;
+ } else {
+ Value =
+ llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
+ ->getValue()
+ .getLimitedValue();
+ }
+
+ // Constant that is not handled.
+ if (Value > 1)
+ return;
+
+ if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
+ OpCode == BinaryOperatorKind::BO_NE))
+ return;
+
+ // Always true, no warnings for that.
+ if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
+ return;
+
+ // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
+ if (Value == 1) {
+ if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
+ return;
+ if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
+ (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
+ return;
+ }
+
+ if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
+ Negation = true;
+ if ((OpCode == BinaryOperatorKind::BO_GT ||
+ OpCode == BinaryOperatorKind::BO_GE) &&
+ ContainerIsLHS)
+ Negation = true;
+ if ((OpCode == BinaryOperatorKind::BO_LT ||
+ OpCode == BinaryOperatorKind::BO_LE) &&
+ !ContainerIsLHS)
+ Negation = true;
+
+ if (Negation)
+ ReplacementText = "!" + ReplacementText;
+ Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
+ ReplacementText);
+
+ } else {
+ // If there is a conversion above the size call to bool, it is safe to just
+ // replace size with empty.
+ if (const auto *UnaryOp =
+ Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
+ Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
+ ReplacementText);
+ else
+ Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+ "!" + ReplacementText);
+ }
+
+ if (MemberCall) {
+ diag(MemberCall->getLocStart(),
+ "the 'empty' method should be used to check "
+ "for emptiness instead of 'size'")
+ << Hint;
+ } else {
+ diag(BinCmp->getLocStart(),
+ "the 'empty' method should be used to check "
+ "for emptiness instead of comparing to an empty object")
+ << Hint;
+ }
+
+ const auto *Container = Result.Nodes.getNodeAs<NamedDecl>("container");
+ const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>("empty");
+
+ diag(Empty->getLocation(), "method %0::empty() defined here",
+ DiagnosticIDs::Note)
+ << Container;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ContainerSizeEmptyCheck.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_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks whether a call to the `size()` method can be replaced with a call to
+/// `empty()`.
+///
+/// The emptiness of a container should be checked using the `empty()` method
+/// instead of the `size()` method. It is not guaranteed that `size()` is a
+/// constant-time function, and it is generally more efficient and also shows
+/// clearer intent to use `empty()`. Furthermore some containers may implement
+/// the `empty()` method but not implement the `size()` method. Using `empty()`
+/// whenever possible makes it easier to switch to another container in the
+/// future.
+class ContainerSizeEmptyCheck : public ClangTidyCheck {
+public:
+ ContainerSizeEmptyCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
--- /dev/null
+//===--- DeleteNullPointerCheck.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 "DeleteNullPointerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) {
+ const auto DeleteExpr =
+ cxxDeleteExpr(has(castExpr(has(declRefExpr(
+ to(decl(equalsBoundNode("deletedPointer"))))))))
+ .bind("deleteExpr");
+
+ const auto DeleteMemberExpr =
+ cxxDeleteExpr(has(castExpr(has(memberExpr(hasDeclaration(
+ fieldDecl(equalsBoundNode("deletedMemberPointer"))))))))
+ .bind("deleteMemberExpr");
+
+ const auto PointerExpr = ignoringImpCasts(anyOf(
+ declRefExpr(to(decl().bind("deletedPointer"))),
+ memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer")))));
+
+ const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean),
+ hasSourceExpression(PointerExpr));
+ const auto BinaryPointerCheckCondition =
+ binaryOperator(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))),
+ hasEitherOperand(PointerExpr));
+
+ Finder->addMatcher(
+ ifStmt(hasCondition(anyOf(PointerCondition, BinaryPointerCheckCondition)),
+ hasThen(anyOf(
+ DeleteExpr, DeleteMemberExpr,
+ compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)),
+ statementCountIs(1))
+ .bind("compound"))))
+ .bind("ifWithDelete"),
+ this);
+}
+
+void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
+ const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
+
+ auto Diag = diag(
+ IfWithDelete->getLocStart(),
+ "'if' statement is unnecessary; deleting null pointer has no effect");
+ if (IfWithDelete->getElse())
+ return;
+ // FIXME: generate fixit for this case.
+
+ Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+ IfWithDelete->getLocStart(),
+ Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getLocEnd(), 0,
+ *Result.SourceManager,
+ Result.Context->getLangOpts())));
+ if (Compound) {
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Compound->getLBracLoc()));
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getTokenRange(Compound->getRBracLoc()));
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DeleteNullPointerCheck.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_READABILITY_DELETE_NULL_POINTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Check whether the 'if' statement is unnecessary before calling 'delete' on a
+/// pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-delete-null-pointer.html
+class DeleteNullPointerCheck : public ClangTidyCheck {
+public:
+ DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
--- /dev/null
+//===--- DeletedDefaultCheck.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 "DeletedDefaultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void DeletedDefaultCheck::registerMatchers(MatchFinder *Finder) {
+ // We match constructors/assignment operators that are:
+ // - explicitly marked '= default'
+ // - actually deleted
+ // - not in template instantiation.
+ // We bind the declaration to "method-decl" and also to "constructor" when
+ // it is a constructor.
+
+ Finder->addMatcher(
+ cxxMethodDecl(anyOf(cxxConstructorDecl().bind("constructor"),
+ isCopyAssignmentOperator(),
+ isMoveAssignmentOperator()),
+ isDefaulted(), unless(isImplicit()), isDeleted(),
+ unless(isInstantiated()))
+ .bind("method-decl"),
+ this);
+}
+
+void DeletedDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+ const StringRef Message = "%0 is explicitly defaulted but implicitly "
+ "deleted, probably because %1; definition can "
+ "either be removed or explicitly deleted";
+ if (const auto *Constructor =
+ Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor")) {
+ auto Diag = diag(Constructor->getLocStart(), Message);
+ if (Constructor->isDefaultConstructor()) {
+ Diag << "default constructor"
+ << "a non-static data member or a base class is lacking a default "
+ "constructor";
+ } else if (Constructor->isCopyConstructor()) {
+ Diag << "copy constructor"
+ << "a non-static data member or a base class is not copyable";
+ } else if (Constructor->isMoveConstructor()) {
+ Diag << "move constructor"
+ << "a non-static data member or a base class is neither copyable "
+ "nor movable";
+ }
+ } else if (const auto *Assignment =
+ Result.Nodes.getNodeAs<CXXMethodDecl>("method-decl")) {
+ diag(Assignment->getLocStart(), Message)
+ << (Assignment->isCopyAssignmentOperator() ? "copy assignment operator"
+ : "move assignment operator")
+ << "a base class or a non-static data member is not assignable, e.g. "
+ "because the latter is marked 'const'";
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DeletedDefaultCheck.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_READABILITY_DELETED_DEFAULT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks when a constructor or an assignment operator is marked as '= default'
+/// but is actually deleted by the compiler.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-deleted-default.html
+class DeletedDefaultCheck : public ClangTidyCheck {
+public:
+ DeletedDefaultCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H
--- /dev/null
+//===--- ElseAfterReturnCheck.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 "ElseAfterReturnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ControlFlowInterruptorMatcher =
+ stmt(anyOf(returnStmt().bind("return"), continueStmt().bind("continue"),
+ breakStmt().bind("break"), cxxThrowExpr().bind("throw")));
+ Finder->addMatcher(
+ compoundStmt(forEach(
+ ifStmt(hasThen(stmt(
+ anyOf(ControlFlowInterruptorMatcher,
+ compoundStmt(has(ControlFlowInterruptorMatcher))))),
+ hasElse(stmt().bind("else")))
+ .bind("if"))),
+ this);
+}
+
+void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+ SourceLocation ElseLoc = If->getElseLoc();
+ std::string ControlFlowInterruptor;
+ for (const auto *BindingName : {"return", "continue", "break", "throw"})
+ if (Result.Nodes.getNodeAs<Stmt>(BindingName))
+ ControlFlowInterruptor = BindingName;
+
+ DiagnosticBuilder Diag = diag(ElseLoc, "do not use 'else' after '%0'")
+ << ControlFlowInterruptor;
+ Diag << tooling::fixit::createRemoval(ElseLoc);
+
+ // FIXME: Removing the braces isn't always safe. Do a more careful analysis.
+ // FIXME: Change clang-format to correctly un-indent the code.
+ if (const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("else"))
+ Diag << tooling::fixit::createRemoval(CS->getLBracLoc())
+ << tooling::fixit::createRemoval(CS->getRBracLoc());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ElseAfterReturnCheck.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_READABILITY_ELSEAFTERRETURNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Flags the usages of `else` after `return`.
+///
+/// http://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return
+class ElseAfterReturnCheck : public ClangTidyCheck {
+public:
+ ElseAfterReturnCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
--- /dev/null
+//===--- FunctionSize.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 "FunctionSizeCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
+ using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+public:
+ bool TraverseStmt(Stmt *Node) {
+ if (!Node)
+ return Base::TraverseStmt(Node);
+
+ if (TrackedParent.back() && !isa<CompoundStmt>(Node))
+ ++Info.Statements;
+
+ switch (Node->getStmtClass()) {
+ case Stmt::IfStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::SwitchStmtClass:
+ ++Info.Branches;
+ LLVM_FALLTHROUGH;
+ case Stmt::CompoundStmtClass:
+ TrackedParent.push_back(true);
+ break;
+ default:
+ TrackedParent.push_back(false);
+ break;
+ }
+
+ Base::TraverseStmt(Node);
+
+ TrackedParent.pop_back();
+
+ return true;
+ }
+
+ bool TraverseCompoundStmt(CompoundStmt *Node) {
+ // If this new compound statement is located in a compound statement, which
+ // is already nested NestingThreshold levels deep, record the start location
+ // of this new compound statement.
+ if (CurrentNestingLevel == Info.NestingThreshold)
+ Info.NestingThresholders.push_back(Node->getLocStart());
+
+ ++CurrentNestingLevel;
+ Base::TraverseCompoundStmt(Node);
+ --CurrentNestingLevel;
+
+ return true;
+ }
+
+ bool TraverseDecl(Decl *Node) {
+ TrackedParent.push_back(false);
+ Base::TraverseDecl(Node);
+ TrackedParent.pop_back();
+ return true;
+ }
+
+ struct FunctionInfo {
+ unsigned Lines = 0;
+ unsigned Statements = 0;
+ unsigned Branches = 0;
+ unsigned NestingThreshold = 0;
+ std::vector<SourceLocation> NestingThresholders;
+ };
+ FunctionInfo Info;
+ std::vector<bool> TrackedParent;
+ unsigned CurrentNestingLevel = 0;
+};
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ LineThreshold(Options.get("LineThreshold", -1U)),
+ StatementThreshold(Options.get("StatementThreshold", 800U)),
+ BranchThreshold(Options.get("BranchThreshold", -1U)),
+ ParameterThreshold(Options.get("ParameterThreshold", -1U)),
+ NestingThreshold(Options.get("NestingThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "LineThreshold", LineThreshold);
+ Options.store(Opts, "StatementThreshold", StatementThreshold);
+ Options.store(Opts, "BranchThreshold", BranchThreshold);
+ Options.store(Opts, "ParameterThreshold", ParameterThreshold);
+ Options.store(Opts, "NestingThreshold", NestingThreshold);
+}
+
+void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
+}
+
+void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ FunctionASTVisitor Visitor;
+ Visitor.Info.NestingThreshold = NestingThreshold;
+ Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
+ auto &FI = Visitor.Info;
+
+ if (FI.Statements == 0)
+ return;
+
+ // Count the lines including whitespace and comments. Really simple.
+ if (const Stmt *Body = Func->getBody()) {
+ SourceManager *SM = Result.SourceManager;
+ if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
+ FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
+ SM->getSpellingLineNumber(Body->getLocStart());
+ }
+ }
+
+ unsigned ActualNumberParameters = Func->getNumParams();
+
+ if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+ FI.Branches > BranchThreshold ||
+ ActualNumberParameters > ParameterThreshold ||
+ !FI.NestingThresholders.empty()) {
+ diag(Func->getLocation(),
+ "function %0 exceeds recommended size/complexity thresholds")
+ << Func;
+ }
+
+ if (FI.Lines > LineThreshold) {
+ diag(Func->getLocation(),
+ "%0 lines including whitespace and comments (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Lines << LineThreshold;
+ }
+
+ if (FI.Statements > StatementThreshold) {
+ diag(Func->getLocation(), "%0 statements (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Statements << StatementThreshold;
+ }
+
+ if (FI.Branches > BranchThreshold) {
+ diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
+ << FI.Branches << BranchThreshold;
+ }
+
+ if (ActualNumberParameters > ParameterThreshold) {
+ diag(Func->getLocation(), "%0 parameters (threshold %1)",
+ DiagnosticIDs::Note)
+ << ActualNumberParameters << ParameterThreshold;
+ }
+
+ for (const auto &CSPos : FI.NestingThresholders) {
+ diag(CSPos, "nesting level %0 starts here (threshold %1)",
+ DiagnosticIDs::Note)
+ << NestingThreshold + 1 << NestingThreshold;
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- FunctionSizeCheck.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_READABILITY_FUNCTIONSIZECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks for large functions based on various metrics.
+///
+/// These options are supported:
+///
+/// * `LineThreshold` - flag functions exceeding this number of lines. The
+/// default is `-1` (ignore the number of lines).
+/// * `StatementThreshold` - flag functions exceeding this number of
+/// statements. This may differ significantly from the number of lines for
+/// macro-heavy code. The default is `800`.
+/// * `BranchThreshold` - flag functions exceeding this number of control
+/// statements. The default is `-1` (ignore the number of branches).
+/// * `ParameterThreshold` - flag functions having a high number of
+/// parameters. The default is `-1` (ignore the number of parameters).
+/// * `NestingThreshold` - flag compound statements which create next nesting
+/// level after `NestingThreshold`. This may differ significantly from the
+/// expected value for macro-heavy code. The default is `-1` (ignore the
+/// nesting level).
+class FunctionSizeCheck : public ClangTidyCheck {
+public:
+ FunctionSizeCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ const unsigned LineThreshold;
+ const unsigned StatementThreshold;
+ const unsigned BranchThreshold;
+ const unsigned ParameterThreshold;
+ const unsigned NestingThreshold;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
--- /dev/null
+//===--- IdentifierNamingCheck.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 "IdentifierNamingCheck.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"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+
+#define DEBUG_TYPE "clang-tidy"
+
+using namespace clang::ast_matchers;
+
+namespace llvm {
+/// Specialisation of DenseMapInfo to allow NamingCheckId objects in DenseMaps
+template <>
+struct DenseMapInfo<
+ clang::tidy::readability::IdentifierNamingCheck::NamingCheckId> {
+ using NamingCheckId =
+ clang::tidy::readability::IdentifierNamingCheck::NamingCheckId;
+
+ static inline NamingCheckId getEmptyKey() {
+ return NamingCheckId(
+ clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-1)),
+ "EMPTY");
+ }
+
+ static inline NamingCheckId getTombstoneKey() {
+ return NamingCheckId(
+ clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-2)),
+ "TOMBSTONE");
+ }
+
+ static unsigned getHashValue(NamingCheckId Val) {
+ assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+ assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+
+ std::hash<NamingCheckId::second_type> SecondHash;
+ return Val.first.getRawEncoding() + SecondHash(Val.second);
+ }
+
+ static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) {
+ if (RHS == getEmptyKey())
+ return LHS == getEmptyKey();
+ if (RHS == getTombstoneKey())
+ return LHS == getTombstoneKey();
+ return LHS == RHS;
+ }
+};
+} // namespace llvm
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+// clang-format off
+#define NAMING_KEYS(m) \
+ m(Namespace) \
+ m(InlineNamespace) \
+ m(EnumConstant) \
+ m(ConstexprVariable) \
+ m(ConstantMember) \
+ m(PrivateMember) \
+ m(ProtectedMember) \
+ m(PublicMember) \
+ m(Member) \
+ m(ClassConstant) \
+ m(ClassMember) \
+ m(GlobalConstant) \
+ m(GlobalVariable) \
+ m(LocalConstant) \
+ m(LocalVariable) \
+ m(StaticConstant) \
+ m(StaticVariable) \
+ m(Constant) \
+ m(Variable) \
+ m(ConstantParameter) \
+ m(ParameterPack) \
+ m(Parameter) \
+ m(AbstractClass) \
+ m(Struct) \
+ m(Class) \
+ m(Union) \
+ m(Enum) \
+ m(GlobalFunction) \
+ m(ConstexprFunction) \
+ m(Function) \
+ m(ConstexprMethod) \
+ m(VirtualMethod) \
+ m(ClassMethod) \
+ m(PrivateMethod) \
+ m(ProtectedMethod) \
+ m(PublicMethod) \
+ m(Method) \
+ m(Typedef) \
+ m(TypeTemplateParameter) \
+ m(ValueTemplateParameter) \
+ m(TemplateTemplateParameter) \
+ m(TemplateParameter) \
+ m(TypeAlias) \
+ m(MacroDefinition) \
+
+enum StyleKind {
+#define ENUMERATE(v) SK_ ## v,
+ NAMING_KEYS(ENUMERATE)
+#undef ENUMERATE
+ SK_Count,
+ SK_Invalid
+};
+
+static StringRef const StyleNames[] = {
+#define STRINGIZE(v) #v,
+ NAMING_KEYS(STRINGIZE)
+#undef STRINGIZE
+};
+
+#undef NAMING_KEYS
+// clang-format on
+
+namespace {
+/// Callback supplies macros to IdentifierNamingCheck::checkMacro
+class IdentifierNamingCheckPPCallbacks : public PPCallbacks {
+public:
+ IdentifierNamingCheckPPCallbacks(Preprocessor *PP,
+ IdentifierNamingCheck *Check)
+ : PP(PP), Check(Check) {}
+
+ /// MacroDefined calls checkMacro for macros in the main file
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override {
+ Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo());
+ }
+
+ /// MacroExpands calls expandMacro for macros in the main file
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange /*Range*/,
+ const MacroArgs * /*Args*/) override {
+ Check->expandMacro(MacroNameTok, MD.getMacroInfo());
+ }
+
+private:
+ Preprocessor *PP;
+ IdentifierNamingCheck *Check;
+};
+} // namespace
+
+IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {
+ auto const fromString = [](StringRef Str) {
+ return llvm::StringSwitch<llvm::Optional<CaseType>>(Str)
+ .Case("aNy_CasE", CT_AnyCase)
+ .Case("lower_case", CT_LowerCase)
+ .Case("UPPER_CASE", CT_UpperCase)
+ .Case("camelBack", CT_CamelBack)
+ .Case("CamelCase", CT_CamelCase)
+ .Case("Camel_Snake_Case", CT_CamelSnakeCase)
+ .Case("camel_Snake_Back", CT_CamelSnakeBack)
+ .Default(llvm::None);
+ };
+
+ for (auto const &Name : StyleNames) {
+ auto const caseOptional =
+ fromString(Options.get((Name + "Case").str(), ""));
+ auto prefix = Options.get((Name + "Prefix").str(), "");
+ auto postfix = Options.get((Name + "Suffix").str(), "");
+
+ if (caseOptional || !prefix.empty() || !postfix.empty()) {
+ NamingStyles.push_back(NamingStyle(caseOptional, prefix, postfix));
+ } else {
+ NamingStyles.push_back(llvm::None);
+ }
+ }
+
+ IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0);
+}
+
+void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ auto const toString = [](CaseType Type) {
+ switch (Type) {
+ case CT_AnyCase:
+ return "aNy_CasE";
+ case CT_LowerCase:
+ return "lower_case";
+ case CT_CamelBack:
+ return "camelBack";
+ case CT_UpperCase:
+ return "UPPER_CASE";
+ case CT_CamelCase:
+ return "CamelCase";
+ case CT_CamelSnakeCase:
+ return "Camel_Snake_Case";
+ case CT_CamelSnakeBack:
+ return "camel_Snake_Back";
+ }
+
+ llvm_unreachable("Unknown Case Type");
+ };
+
+ for (size_t i = 0; i < SK_Count; ++i) {
+ if (NamingStyles[i]) {
+ if (NamingStyles[i]->Case) {
+ Options.store(Opts, (StyleNames[i] + "Case").str(),
+ toString(*NamingStyles[i]->Case));
+ }
+ Options.store(Opts, (StyleNames[i] + "Prefix").str(),
+ NamingStyles[i]->Prefix);
+ Options.store(Opts, (StyleNames[i] + "Suffix").str(),
+ NamingStyles[i]->Suffix);
+ }
+ }
+
+ Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
+}
+
+void IdentifierNamingCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(namedDecl().bind("decl"), this);
+ Finder->addMatcher(usingDecl().bind("using"), this);
+ Finder->addMatcher(declRefExpr().bind("declRef"), this);
+ Finder->addMatcher(cxxConstructorDecl().bind("classRef"), this);
+ Finder->addMatcher(cxxDestructorDecl().bind("classRef"), this);
+ Finder->addMatcher(typeLoc().bind("typeLoc"), this);
+ Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this);
+}
+
+void IdentifierNamingCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ llvm::make_unique<IdentifierNamingCheckPPCallbacks>(
+ &Compiler.getPreprocessor(), this));
+}
+
+static bool matchesStyle(StringRef Name,
+ IdentifierNamingCheck::NamingStyle Style) {
+ static llvm::Regex Matchers[] = {
+ llvm::Regex("^.*$"),
+ llvm::Regex("^[a-z][a-z0-9_]*$"),
+ llvm::Regex("^[a-z][a-zA-Z0-9]*$"),
+ llvm::Regex("^[A-Z][A-Z0-9_]*$"),
+ llvm::Regex("^[A-Z][a-zA-Z0-9]*$"),
+ llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"),
+ llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"),
+ };
+
+ bool Matches = true;
+ if (Name.startswith(Style.Prefix))
+ Name = Name.drop_front(Style.Prefix.size());
+ else
+ Matches = false;
+
+ if (Name.endswith(Style.Suffix))
+ Name = Name.drop_back(Style.Suffix.size());
+ else
+ Matches = false;
+
+ // Ensure the name doesn't have any extra underscores beyond those specified
+ // in the prefix and suffix.
+ if (Name.startswith("_") || Name.endswith("_"))
+ Matches = false;
+
+ if (Style.Case && !Matchers[static_cast<size_t>(*Style.Case)].match(Name))
+ Matches = false;
+
+ return Matches;
+}
+
+static std::string fixupWithCase(StringRef Name,
+ IdentifierNamingCheck::CaseType Case) {
+ static llvm::Regex Splitter(
+ "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
+
+ SmallVector<StringRef, 8> Substrs;
+ Name.split(Substrs, "_", -1, false);
+
+ SmallVector<StringRef, 8> Words;
+ for (auto Substr : Substrs) {
+ while (!Substr.empty()) {
+ SmallVector<StringRef, 8> Groups;
+ if (!Splitter.match(Substr, &Groups))
+ break;
+
+ if (Groups[2].size() > 0) {
+ Words.push_back(Groups[1]);
+ Substr = Substr.substr(Groups[0].size());
+ } else if (Groups[3].size() > 0) {
+ Words.push_back(Groups[3]);
+ Substr = Substr.substr(Groups[0].size() - Groups[4].size());
+ } else if (Groups[5].size() > 0) {
+ Words.push_back(Groups[5]);
+ Substr = Substr.substr(Groups[0].size() - Groups[6].size());
+ }
+ }
+ }
+
+ if (Words.empty())
+ return Name;
+
+ std::string Fixup;
+ switch (Case) {
+ case IdentifierNamingCheck::CT_AnyCase:
+ Fixup += Name;
+ break;
+
+ case IdentifierNamingCheck::CT_LowerCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += Word.lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_UpperCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += Word.upper();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelCase:
+ for (auto const &Word : Words) {
+ Fixup += Word.substr(0, 1).upper();
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelBack:
+ for (auto const &Word : Words) {
+ if (&Word == &Words.front()) {
+ Fixup += Word.lower();
+ } else {
+ Fixup += Word.substr(0, 1).upper();
+ Fixup += Word.substr(1).lower();
+ }
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelSnakeCase:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front())
+ Fixup += "_";
+ Fixup += Word.substr(0, 1).upper();
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+
+ case IdentifierNamingCheck::CT_CamelSnakeBack:
+ for (auto const &Word : Words) {
+ if (&Word != &Words.front()) {
+ Fixup += "_";
+ Fixup += Word.substr(0, 1).upper();
+ } else {
+ Fixup += Word.substr(0, 1).lower();
+ }
+ Fixup += Word.substr(1).lower();
+ }
+ break;
+ }
+
+ return Fixup;
+}
+
+static std::string
+fixupWithStyle(StringRef Name,
+ const IdentifierNamingCheck::NamingStyle &Style) {
+ const std::string Fixed = fixupWithCase(
+ Name, Style.Case.getValueOr(IdentifierNamingCheck::CaseType::CT_AnyCase));
+ StringRef Mid = StringRef(Fixed).trim("_");
+ if (Mid.empty())
+ Mid = "_";
+ return (Style.Prefix + Mid + Style.Suffix).str();
+}
+
+static StyleKind findStyleKind(
+ const NamedDecl *D,
+ const std::vector<llvm::Optional<IdentifierNamingCheck::NamingStyle>>
+ &NamingStyles) {
+ if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef])
+ return SK_Typedef;
+
+ if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
+ return SK_TypeAlias;
+
+ if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
+ if (Decl->isAnonymousNamespace())
+ return SK_Invalid;
+
+ if (Decl->isInline() && NamingStyles[SK_InlineNamespace])
+ return SK_InlineNamespace;
+
+ if (NamingStyles[SK_Namespace])
+ return SK_Namespace;
+ }
+
+ if (isa<EnumDecl>(D) && NamingStyles[SK_Enum])
+ return SK_Enum;
+
+ if (isa<EnumConstantDecl>(D)) {
+ if (NamingStyles[SK_EnumConstant])
+ return SK_EnumConstant;
+
+ if (NamingStyles[SK_Constant])
+ return SK_Constant;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) {
+ if (Decl->isAnonymousStructOrUnion())
+ return SK_Invalid;
+
+ if (!Decl->getCanonicalDecl()->isThisDeclarationADefinition())
+ return SK_Invalid;
+
+ if (Decl->hasDefinition() && Decl->isAbstract() &&
+ NamingStyles[SK_AbstractClass])
+ return SK_AbstractClass;
+
+ if (Decl->isStruct() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Decl->isStruct() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Decl->isClass() && NamingStyles[SK_Class])
+ return SK_Class;
+
+ if (Decl->isClass() && NamingStyles[SK_Struct])
+ return SK_Struct;
+
+ if (Decl->isUnion() && NamingStyles[SK_Union])
+ return SK_Union;
+
+ if (Decl->isEnum() && NamingStyles[SK_Enum])
+ return SK_Enum;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<FieldDecl>(D)) {
+ QualType Type = Decl->getType();
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ NamingStyles[SK_ConstantMember])
+ return SK_ConstantMember;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ NamingStyles[SK_Constant])
+ return SK_Constant;
+
+ if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
+ return SK_PrivateMember;
+
+ if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
+ return SK_ProtectedMember;
+
+ if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember])
+ return SK_PublicMember;
+
+ if (NamingStyles[SK_Member])
+ return SK_Member;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
+ QualType Type = Decl->getType();
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+ return SK_ConstexprVariable;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ NamingStyles[SK_ConstantParameter])
+ return SK_ConstantParameter;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ NamingStyles[SK_Constant])
+ return SK_Constant;
+
+ if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack])
+ return SK_ParameterPack;
+
+ if (NamingStyles[SK_Parameter])
+ return SK_Parameter;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<VarDecl>(D)) {
+ QualType Type = Decl->getType();
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+ return SK_ConstexprVariable;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant])
+ return SK_ClassConstant;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
+ return SK_GlobalConstant;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ Decl->isStaticLocal() && NamingStyles[SK_StaticConstant])
+ return SK_StaticConstant;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
+ return SK_LocalConstant;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
+ return SK_LocalConstant;
+
+ if (!Type.isNull() && Type.isLocalConstQualified() &&
+ NamingStyles[SK_Constant])
+ return SK_Constant;
+
+ if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember])
+ return SK_ClassMember;
+
+ if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
+ return SK_GlobalVariable;
+
+ if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable])
+ return SK_StaticVariable;
+
+ if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
+ return SK_LocalVariable;
+
+ if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
+ return SK_LocalVariable;
+
+ if (NamingStyles[SK_Variable])
+ return SK_Variable;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) {
+ if (Decl->isMain() || !Decl->isUserProvided() ||
+ Decl->isUsualDeallocationFunction() ||
+ Decl->isCopyAssignmentOperator() || Decl->isMoveAssignmentOperator() ||
+ Decl->size_overridden_methods() > 0)
+ return SK_Invalid;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod])
+ return SK_ConstexprMethod;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+ return SK_ConstexprFunction;
+
+ if (Decl->isStatic() && NamingStyles[SK_ClassMethod])
+ return SK_ClassMethod;
+
+ if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod])
+ return SK_VirtualMethod;
+
+ if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod])
+ return SK_PrivateMethod;
+
+ if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod])
+ return SK_ProtectedMethod;
+
+ if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod])
+ return SK_PublicMethod;
+
+ if (NamingStyles[SK_Method])
+ return SK_Method;
+
+ if (NamingStyles[SK_Function])
+ return SK_Function;
+
+ return SK_Invalid;
+ }
+
+ if (const auto *Decl = dyn_cast<FunctionDecl>(D)) {
+ if (Decl->isMain())
+ return SK_Invalid;
+
+ if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+ return SK_ConstexprFunction;
+
+ if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction])
+ return SK_GlobalFunction;
+
+ if (NamingStyles[SK_Function])
+ return SK_Function;
+ }
+
+ if (isa<TemplateTypeParmDecl>(D)) {
+ if (NamingStyles[SK_TypeTemplateParameter])
+ return SK_TypeTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ if (isa<NonTypeTemplateParmDecl>(D)) {
+ if (NamingStyles[SK_ValueTemplateParameter])
+ return SK_ValueTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ if (isa<TemplateTemplateParmDecl>(D)) {
+ if (NamingStyles[SK_TemplateTemplateParameter])
+ return SK_TemplateTemplateParameter;
+
+ if (NamingStyles[SK_TemplateParameter])
+ return SK_TemplateParameter;
+
+ return SK_Invalid;
+ }
+
+ return SK_Invalid;
+}
+
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+ const IdentifierNamingCheck::NamingCheckId &Decl,
+ SourceRange Range, SourceManager *SourceMgr = nullptr) {
+ // Do nothing if the provided range is invalid.
+ if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid())
+ return;
+
+ // If we have a source manager, use it to convert to the spelling location for
+ // performing the fix. This is necessary because macros can map the same
+ // spelling location to different source locations, and we only want to fix
+ // the token once, before it is expanded by the macro.
+ SourceLocation FixLocation = Range.getBegin();
+ if (SourceMgr)
+ FixLocation = SourceMgr->getSpellingLoc(FixLocation);
+ if (FixLocation.isInvalid())
+ return;
+
+ // Try to insert the identifier location in the Usages map, and bail out if it
+ // is already in there
+ auto &Failure = Failures[Decl];
+ if (!Failure.RawUsageLocs.insert(FixLocation.getRawEncoding()).second)
+ return;
+
+ if (!Failure.ShouldFix)
+ return;
+
+ // Check if the range is entirely contained within a macro argument.
+ SourceLocation MacroArgExpansionStartForRangeBegin;
+ SourceLocation MacroArgExpansionStartForRangeEnd;
+ bool RangeIsEntirelyWithinMacroArgument =
+ SourceMgr &&
+ SourceMgr->isMacroArgExpansion(Range.getBegin(),
+ &MacroArgExpansionStartForRangeBegin) &&
+ SourceMgr->isMacroArgExpansion(Range.getEnd(),
+ &MacroArgExpansionStartForRangeEnd) &&
+ MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd;
+
+ // Check if the range contains any locations from a macro expansion.
+ bool RangeContainsMacroExpansion = RangeIsEntirelyWithinMacroArgument ||
+ Range.getBegin().isMacroID() ||
+ Range.getEnd().isMacroID();
+
+ bool RangeCanBeFixed =
+ RangeIsEntirelyWithinMacroArgument || !RangeContainsMacroExpansion;
+ Failure.ShouldFix = RangeCanBeFixed;
+}
+
+/// Convenience method when the usage to be added is a NamedDecl
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+ const NamedDecl *Decl, SourceRange Range,
+ SourceManager *SourceMgr = nullptr) {
+ return addUsage(Failures, IdentifierNamingCheck::NamingCheckId(
+ Decl->getLocation(), Decl->getNameAsString()),
+ Range, SourceMgr);
+}
+
+void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Decl =
+ Result.Nodes.getNodeAs<CXXConstructorDecl>("classRef")) {
+ if (Decl->isImplicit())
+ return;
+
+ addUsage(NamingCheckFailures, Decl->getParent(),
+ Decl->getNameInfo().getSourceRange());
+
+ for (const auto *Init : Decl->inits()) {
+ if (!Init->isWritten() || Init->isInClassMemberInitializer())
+ continue;
+ if (const auto *FD = Init->getAnyMember())
+ addUsage(NamingCheckFailures, FD, SourceRange(Init->getMemberLocation()));
+ // Note: delegating constructors and base class initializers are handled
+ // via the "typeLoc" matcher.
+ }
+ return;
+ }
+
+ if (const auto *Decl =
+ Result.Nodes.getNodeAs<CXXDestructorDecl>("classRef")) {
+ if (Decl->isImplicit())
+ return;
+
+ SourceRange Range = Decl->getNameInfo().getSourceRange();
+ if (Range.getBegin().isInvalid())
+ return;
+ // The first token that will be found is the ~ (or the equivalent trigraph),
+ // we want instead to replace the next token, that will be the identifier.
+ Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());
+
+ addUsage(NamingCheckFailures, Decl->getParent(), Range);
+ return;
+ }
+
+ if (const auto *Loc = Result.Nodes.getNodeAs<TypeLoc>("typeLoc")) {
+ NamedDecl *Decl = nullptr;
+ if (const auto &Ref = Loc->getAs<TagTypeLoc>()) {
+ Decl = Ref.getDecl();
+ } else if (const auto &Ref = Loc->getAs<InjectedClassNameTypeLoc>()) {
+ Decl = Ref.getDecl();
+ } else if (const auto &Ref = Loc->getAs<UnresolvedUsingTypeLoc>()) {
+ Decl = Ref.getDecl();
+ } else if (const auto &Ref = Loc->getAs<TemplateTypeParmTypeLoc>()) {
+ Decl = Ref.getDecl();
+ }
+
+ if (Decl) {
+ addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
+ return;
+ }
+
+ if (const auto &Ref = Loc->getAs<TemplateSpecializationTypeLoc>()) {
+ const auto *Decl =
+ Ref.getTypePtr()->getTemplateName().getAsTemplateDecl();
+
+ SourceRange Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc());
+ if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) {
+ if (const auto *TemplDecl = ClassDecl->getTemplatedDecl())
+ addUsage(NamingCheckFailures, TemplDecl, Range);
+ return;
+ }
+ }
+
+ if (const auto &Ref =
+ Loc->getAs<DependentTemplateSpecializationTypeLoc>()) {
+ if (const auto *Decl = Ref.getTypePtr()->getAsTagDecl())
+ addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
+ return;
+ }
+ }
+
+ if (const auto *Loc =
+ Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("nestedNameLoc")) {
+ if (NestedNameSpecifier *Spec = Loc->getNestedNameSpecifier()) {
+ if (NamespaceDecl *Decl = Spec->getAsNamespace()) {
+ addUsage(NamingCheckFailures, Decl, Loc->getLocalSourceRange());
+ return;
+ }
+ }
+ }
+
+ if (const auto *Decl = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+ for (const auto &Shadow : Decl->shadows()) {
+ addUsage(NamingCheckFailures, Shadow->getTargetDecl(),
+ Decl->getNameInfo().getSourceRange());
+ }
+ return;
+ }
+
+ if (const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declRef")) {
+ SourceRange Range = DeclRef->getNameInfo().getSourceRange();
+ addUsage(NamingCheckFailures, DeclRef->getDecl(), Range,
+ Result.SourceManager);
+ return;
+ }
+
+ if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) {
+ if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit())
+ return;
+
+ // Fix type aliases in value declarations
+ if (const auto *Value = Result.Nodes.getNodeAs<ValueDecl>("decl")) {
+ if (const auto *Typedef =
+ Value->getType().getTypePtr()->getAs<TypedefType>()) {
+ addUsage(NamingCheckFailures, Typedef->getDecl(),
+ Value->getSourceRange());
+ }
+ }
+
+ // Fix type aliases in function declarations
+ if (const auto *Value = Result.Nodes.getNodeAs<FunctionDecl>("decl")) {
+ if (const auto *Typedef =
+ Value->getReturnType().getTypePtr()->getAs<TypedefType>()) {
+ addUsage(NamingCheckFailures, Typedef->getDecl(),
+ Value->getSourceRange());
+ }
+ for (unsigned i = 0; i < Value->getNumParams(); ++i) {
+ if (const auto *Typedef = Value->parameters()[i]
+ ->getType()
+ .getTypePtr()
+ ->getAs<TypedefType>()) {
+ addUsage(NamingCheckFailures, Typedef->getDecl(),
+ Value->getSourceRange());
+ }
+ }
+ }
+
+ // Ignore ClassTemplateSpecializationDecl which are creating duplicate
+ // replacements with CXXRecordDecl
+ if (isa<ClassTemplateSpecializationDecl>(Decl))
+ return;
+
+ StyleKind SK = findStyleKind(Decl, NamingStyles);
+ if (SK == SK_Invalid)
+ return;
+
+ if (!NamingStyles[SK])
+ return;
+
+ const NamingStyle &Style = *NamingStyles[SK];
+ StringRef Name = Decl->getName();
+ if (matchesStyle(Name, Style))
+ return;
+
+ std::string KindName = fixupWithCase(StyleNames[SK], CT_LowerCase);
+ std::replace(KindName.begin(), KindName.end(), '_', ' ');
+
+ std::string Fixup = fixupWithStyle(Name, Style);
+ if (StringRef(Fixup).equals(Name)) {
+ if (!IgnoreFailedSplit) {
+ DEBUG(llvm::dbgs()
+ << Decl->getLocStart().printToString(*Result.SourceManager)
+ << llvm::format(": unable to split words for %s '%s'\n",
+ KindName.c_str(), Name.str().c_str()));
+ }
+ } else {
+ NamingCheckFailure &Failure = NamingCheckFailures[NamingCheckId(
+ Decl->getLocation(), Decl->getNameAsString())];
+ SourceRange Range =
+ DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
+ .getSourceRange();
+
+ Failure.Fixup = std::move(Fixup);
+ Failure.KindName = std::move(KindName);
+ addUsage(NamingCheckFailures, Decl, Range);
+ }
+ }
+}
+
+void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr,
+ const Token &MacroNameTok,
+ const MacroInfo *MI) {
+ if (!NamingStyles[SK_MacroDefinition])
+ return;
+
+ StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
+ const NamingStyle &Style = *NamingStyles[SK_MacroDefinition];
+ if (matchesStyle(Name, Style))
+ return;
+
+ std::string KindName =
+ fixupWithCase(StyleNames[SK_MacroDefinition], CT_LowerCase);
+ std::replace(KindName.begin(), KindName.end(), '_', ' ');
+
+ std::string Fixup = fixupWithStyle(Name, Style);
+ if (StringRef(Fixup).equals(Name)) {
+ if (!IgnoreFailedSplit) {
+ DEBUG(
+ llvm::dbgs() << MacroNameTok.getLocation().printToString(SourceMgr)
+ << llvm::format(": unable to split words for %s '%s'\n",
+ KindName.c_str(), Name.str().c_str()));
+ }
+ } else {
+ NamingCheckId ID(MI->getDefinitionLoc(), Name);
+ NamingCheckFailure &Failure = NamingCheckFailures[ID];
+ SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
+
+ Failure.Fixup = std::move(Fixup);
+ Failure.KindName = std::move(KindName);
+ addUsage(NamingCheckFailures, ID, Range);
+ }
+}
+
+void IdentifierNamingCheck::expandMacro(const Token &MacroNameTok,
+ const MacroInfo *MI) {
+ StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
+ NamingCheckId ID(MI->getDefinitionLoc(), Name);
+
+ auto Failure = NamingCheckFailures.find(ID);
+ if (Failure == NamingCheckFailures.end())
+ return;
+
+ SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
+ addUsage(NamingCheckFailures, ID, Range);
+}
+
+void IdentifierNamingCheck::onEndOfTranslationUnit() {
+ for (const auto &Pair : NamingCheckFailures) {
+ const NamingCheckId &Decl = Pair.first;
+ const NamingCheckFailure &Failure = Pair.second;
+
+ if (Failure.KindName.empty())
+ continue;
+
+ if (Failure.ShouldFix) {
+ auto Diag = diag(Decl.first, "invalid case style for %0 '%1'")
+ << Failure.KindName << Decl.second;
+
+ for (const auto &Loc : Failure.RawUsageLocs) {
+ // We assume that the identifier name is made of one token only. This is
+ // always the case as we ignore usages in macros that could build
+ // identifier names by combining multiple tokens.
+ //
+ // For destructors, we alread take care of it by remembering the
+ // location of the start of the identifier and not the start of the
+ // tilde.
+ //
+ // Other multi-token identifiers, such as operators are not checked at
+ // all.
+ Diag << FixItHint::CreateReplacement(
+ SourceRange(SourceLocation::getFromRawEncoding(Loc)),
+ Failure.Fixup);
+ }
+ }
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- IdentifierNamingCheck.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_READABILITY_IDENTIFIERNAMINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+
+class MacroInfo;
+
+namespace tidy {
+namespace readability {
+
+/// Checks for identifiers naming style mismatch.
+///
+/// This check will try to enforce coding guidelines on the identifiers naming.
+/// It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing
+/// and tries to convert from one to another if a mismatch is detected.
+///
+/// It also supports a fixed prefix and suffix that will be prepended or
+/// appended to the identifiers, regardless of the casing.
+///
+/// Many configuration options are available, in order to be able to create
+/// different rules for different kind of identifier. In general, the
+/// rules are falling back to a more generic rule if the specific case is not
+/// configured.
+class IdentifierNamingCheck : public ClangTidyCheck {
+public:
+ IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+ void onEndOfTranslationUnit() override;
+
+ enum CaseType {
+ CT_AnyCase = 0,
+ CT_LowerCase,
+ CT_CamelBack,
+ CT_UpperCase,
+ CT_CamelCase,
+ CT_CamelSnakeCase,
+ CT_CamelSnakeBack
+ };
+
+ struct NamingStyle {
+ NamingStyle() = default;
+
+ NamingStyle(llvm::Optional<CaseType> Case, const std::string &Prefix,
+ const std::string &Suffix)
+ : Case(Case), Prefix(Prefix), Suffix(Suffix) {}
+
+ llvm::Optional<CaseType> Case;
+ std::string Prefix;
+ std::string Suffix;
+ };
+
+ /// \brief Holds an identifier name check failure, tracking the kind of the
+ /// identifer, its possible fixup and the starting locations of all the
+ /// identifier usages.
+ struct NamingCheckFailure {
+ std::string KindName;
+ std::string Fixup;
+
+ /// \brief Whether the failure should be fixed or not.
+ ///
+ /// ie: if the identifier was used or declared within a macro we won't offer
+ /// a fixup for safety reasons.
+ bool ShouldFix;
+
+ /// \brief A set of all the identifier usages starting SourceLocation, in
+ /// their encoded form.
+ llvm::DenseSet<unsigned> RawUsageLocs;
+
+ NamingCheckFailure() : ShouldFix(true) {}
+ };
+
+ typedef std::pair<SourceLocation, std::string> NamingCheckId;
+
+ typedef llvm::DenseMap<NamingCheckId, NamingCheckFailure>
+ NamingCheckFailureMap;
+
+ /// Check Macros for style violations.
+ void checkMacro(SourceManager &sourceMgr, const Token &MacroNameTok,
+ const MacroInfo *MI);
+
+ /// Add a usage of a macro if it already has a violation.
+ void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
+
+private:
+ std::vector<llvm::Optional<NamingStyle>> NamingStyles;
+ bool IgnoreFailedSplit;
+ NamingCheckFailureMap NamingCheckFailures;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
--- /dev/null
+//===--- ImplicitBoolCastCheck.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 "ImplicitBoolCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include <queue>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+AST_MATCHER(Stmt, isMacroExpansion) {
+ SourceManager &SM = Finder->getASTContext().getSourceManager();
+ SourceLocation Loc = Node.getLocStart();
+ return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
+ SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LO = Context.getLangOpts();
+ SourceLocation Loc = Statement->getLocStart();
+ return SM.isMacroBodyExpansion(Loc) &&
+ Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
+}
+
+AST_MATCHER(Stmt, isNULLMacroExpansion) {
+ return isNULLMacroExpansion(&Node, Finder->getASTContext());
+}
+
+StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
+ QualType Type,
+ ASTContext &Context) {
+ switch (CastExprKind) {
+ case CK_IntegralToBoolean:
+ return Type->isUnsignedIntegerType() ? "0u" : "0";
+
+ case CK_FloatingToBoolean:
+ return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
+
+ case CK_PointerToBoolean:
+ case CK_MemberPointerToBoolean: // Fall-through on purpose.
+ return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
+
+ default:
+ llvm_unreachable("Unexpected cast kind");
+ }
+}
+
+bool isUnaryLogicalNotOperator(const Stmt *Statement) {
+ const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
+ return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
+}
+
+bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
+ switch (OperatorKind) {
+ case OO_New:
+ case OO_Delete: // Fall-through on purpose.
+ case OO_Array_New:
+ case OO_Array_Delete:
+ case OO_ArrowStar:
+ case OO_Arrow:
+ case OO_Call:
+ case OO_Subscript:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+bool areParensNeededForStatement(const Stmt *Statement) {
+ if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
+ return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
+ }
+
+ return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
+}
+
+void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast, const Stmt *Parent,
+ ASTContext &Context) {
+ // In case of expressions like (! integer), we should remove the redundant not
+ // operator and use inverted comparison (integer == 0).
+ bool InvertComparison =
+ Parent != nullptr && isUnaryLogicalNotOperator(Parent);
+ if (InvertComparison) {
+ SourceLocation ParentStartLoc = Parent->getLocStart();
+ SourceLocation ParentEndLoc =
+ cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
+ Diag << FixItHint::CreateRemoval(
+ CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
+
+ Parent = Context.getParents(*Parent)[0].get<Stmt>();
+ }
+
+ const Expr *SubExpr = Cast->getSubExpr();
+
+ bool NeedInnerParens = areParensNeededForStatement(SubExpr);
+ bool NeedOuterParens =
+ Parent != nullptr && areParensNeededForStatement(Parent);
+
+ std::string StartLocInsertion;
+
+ if (NeedOuterParens) {
+ StartLocInsertion += "(";
+ }
+ if (NeedInnerParens) {
+ StartLocInsertion += "(";
+ }
+
+ if (!StartLocInsertion.empty()) {
+ Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
+ }
+
+ std::string EndLocInsertion;
+
+ if (NeedInnerParens) {
+ EndLocInsertion += ")";
+ }
+
+ if (InvertComparison) {
+ EndLocInsertion += " == ";
+ } else {
+ EndLocInsertion += " != ";
+ }
+
+ EndLocInsertion += getZeroLiteralToCompareWithForType(
+ Cast->getCastKind(), SubExpr->getType(), Context);
+
+ if (NeedOuterParens) {
+ EndLocInsertion += ")";
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
+ Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
+}
+
+StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
+ ASTContext &Context) {
+ if (isNULLMacroExpansion(Expression, Context)) {
+ return "false";
+ }
+
+ if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
+ return (IntLit->getValue() == 0) ? "false" : "true";
+ }
+
+ if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
+ llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
+ FloatLitAbsValue.clearSign();
+ return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
+ }
+
+ if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
+ return (CharLit->getValue() == 0) ? "false" : "true";
+ }
+
+ if (isa<StringLiteral>(Expression->IgnoreCasts())) {
+ return "true";
+ }
+
+ return StringRef();
+}
+
+void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
+ const ImplicitCastExpr *Cast,
+ ASTContext &Context, StringRef OtherType) {
+ const Expr *SubExpr = Cast->getSubExpr();
+ bool NeedParens = !isa<ParenExpr>(SubExpr);
+
+ Diag << FixItHint::CreateInsertion(
+ Cast->getLocStart(),
+ (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
+ .str());
+
+ if (NeedParens) {
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Cast->getLocEnd(), 0, Context.getSourceManager(),
+ Context.getLangOpts());
+
+ Diag << FixItHint::CreateInsertion(EndLoc, ")");
+ }
+}
+
+StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
+ QualType DestType, ASTContext &Context) {
+ // Prior to C++11, false literal could be implicitly converted to pointer.
+ if (!Context.getLangOpts().CPlusPlus11 &&
+ (DestType->isPointerType() || DestType->isMemberPointerType()) &&
+ BoolLiteral->getValue() == false) {
+ return "0";
+ }
+
+ if (DestType->isFloatingType()) {
+ if (Context.hasSameType(DestType, Context.FloatTy)) {
+ return BoolLiteral->getValue() ? "1.0f" : "0.0f";
+ }
+ return BoolLiteral->getValue() ? "1.0" : "0.0";
+ }
+
+ if (DestType->isUnsignedIntegerType()) {
+ return BoolLiteral->getValue() ? "1u" : "0u";
+ }
+ return BoolLiteral->getValue() ? "1" : "0";
+}
+
+bool isAllowedConditionalCast(const ImplicitCastExpr *Cast,
+ ASTContext &Context) {
+ std::queue<const Stmt *> Q;
+ Q.push(Cast);
+ while (!Q.empty()) {
+ for (const auto &N : Context.getParents(*Q.front())) {
+ const Stmt *S = N.get<Stmt>();
+ if (!S)
+ return false;
+ if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
+ isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
+ return true;
+ if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
+ isUnaryLogicalNotOperator(S) ||
+ (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
+ Q.push(S);
+ } else {
+ return false;
+ }
+ }
+ Q.pop();
+ }
+ return false;
+}
+
+} // anonymous namespace
+
+ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllowConditionalIntegerCasts(
+ Options.get("AllowConditionalIntegerCasts", false)),
+ AllowConditionalPointerCasts(
+ Options.get("AllowConditionalPointerCasts", false)) {}
+
+void ImplicitBoolCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowConditionalIntegerCasts",
+ AllowConditionalIntegerCasts);
+ Options.store(Opts, "AllowConditionalPointerCasts",
+ AllowConditionalPointerCasts);
+}
+
+void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
+ // This check doesn't make much sense if we run it on language without
+ // built-in bool support.
+ if (!getLangOpts().Bool) {
+ return;
+ }
+
+ auto exceptionCases =
+ expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
+ hasParent(explicitCastExpr())));
+ auto implicitCastFromBool = implicitCastExpr(
+ anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
+ // Prior to C++11 cast from bool literal to pointer was allowed.
+ allOf(anyOf(hasCastKind(CK_NullToPointer),
+ hasCastKind(CK_NullToMemberPointer)),
+ hasSourceExpression(cxxBoolLiteral()))),
+ hasSourceExpression(expr(hasType(booleanType()))),
+ unless(exceptionCases));
+ auto boolXor =
+ binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
+ hasRHS(implicitCastFromBool));
+ Finder->addMatcher(
+ implicitCastExpr(
+ anyOf(hasCastKind(CK_IntegralToBoolean),
+ hasCastKind(CK_FloatingToBoolean),
+ hasCastKind(CK_PointerToBoolean),
+ hasCastKind(CK_MemberPointerToBoolean)),
+ // Exclude case of using if or while statements with variable
+ // declaration, e.g.:
+ // if (int var = functionCall()) {}
+ unless(
+ hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
+ // Exclude cases common to implicit cast to and from bool.
+ unless(exceptionCases), unless(has(boolXor)),
+ // Retrive also parent statement, to check if we need additional
+ // parens in replacement.
+ anyOf(hasParent(stmt().bind("parentStmt")), anything()),
+ unless(isInTemplateInstantiation()),
+ unless(hasAncestor(functionTemplateDecl())))
+ .bind("implicitCastToBool"),
+ this);
+
+ auto boolComparison = binaryOperator(
+ anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+ hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
+ auto boolOpAssignment =
+ binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
+ hasLHS(expr(hasType(booleanType()))));
+ Finder->addMatcher(
+ implicitCastExpr(
+ implicitCastFromBool,
+ // Exclude comparisons of bools, as they are always cast to integers
+ // in such context:
+ // bool_expr_a == bool_expr_b
+ // bool_expr_a != bool_expr_b
+ unless(hasParent(binaryOperator(
+ anyOf(boolComparison, boolXor, boolOpAssignment)))),
+ // Check also for nested casts, for example: bool -> int -> float.
+ anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
+ anything()),
+ unless(isInTemplateInstantiation()),
+ unless(hasAncestor(functionTemplateDecl())))
+ .bind("implicitCastFromBool"),
+ this);
+}
+
+void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *CastToBool =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
+ const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
+ return handleCastToBool(CastToBool, Parent, *Result.Context);
+ }
+
+ if (const auto *CastFromBool =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
+ const auto *NextImplicitCast =
+ Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
+ return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
+ }
+}
+
+void ImplicitBoolCastCheck::handleCastToBool(const ImplicitCastExpr *Cast,
+ const Stmt *Parent,
+ ASTContext &Context) {
+ if (AllowConditionalPointerCasts &&
+ (Cast->getCastKind() == CK_PointerToBoolean ||
+ Cast->getCastKind() == CK_MemberPointerToBoolean) &&
+ isAllowedConditionalCast(Cast, Context)) {
+ return;
+ }
+
+ if (AllowConditionalIntegerCasts &&
+ Cast->getCastKind() == CK_IntegralToBoolean &&
+ isAllowedConditionalCast(Cast, Context)) {
+ return;
+ }
+
+ auto Diag = diag(Cast->getLocStart(), "implicit cast %0 -> bool")
+ << Cast->getSubExpr()->getType();
+
+ StringRef EquivalentLiteral =
+ getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
+ if (!EquivalentLiteral.empty()) {
+ Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
+ } else {
+ fixGenericExprCastToBool(Diag, Cast, Parent, Context);
+ }
+}
+
+void ImplicitBoolCastCheck::handleCastFromBool(
+ const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
+ ASTContext &Context) {
+ QualType DestType =
+ NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
+ auto Diag = diag(Cast->getLocStart(), "implicit cast bool -> %0") << DestType;
+
+ if (const auto *BoolLiteral =
+ dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
+ Diag << tooling::fixit::createReplacement(
+ *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
+ } else {
+ fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ImplicitBoolCastCheck.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_READABILITY_IMPLICIT_BOOL_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks for use of implicit bool casts in expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-cast.html
+class ImplicitBoolCastCheck : public ClangTidyCheck {
+public:
+ ImplicitBoolCastCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void handleCastToBool(const ImplicitCastExpr *CastExpression,
+ const Stmt *ParentStatement, ASTContext &Context);
+ void handleCastFromBool(const ImplicitCastExpr *CastExpression,
+ const ImplicitCastExpr *FurtherImplicitCastExpression,
+ ASTContext &Context);
+
+ bool AllowConditionalIntegerCasts;
+ bool AllowConditionalPointerCasts;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
--- /dev/null
+//===--- InconsistentDeclarationParameterNameCheck.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 "InconsistentDeclarationParameterNameCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+#include <algorithm>
+#include <functional>
+#include <sstream>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
+ auto It = Node.redecls_begin();
+ auto EndIt = Node.redecls_end();
+
+ if (It == EndIt)
+ return false;
+
+ ++It;
+ return It != EndIt;
+}
+
+struct DifferingParamInfo {
+ DifferingParamInfo(StringRef SourceName, StringRef OtherName,
+ SourceRange OtherNameRange, bool GenerateFixItHint)
+ : SourceName(SourceName), OtherName(OtherName),
+ OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
+
+ StringRef SourceName;
+ StringRef OtherName;
+ SourceRange OtherNameRange;
+ bool GenerateFixItHint;
+};
+
+using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
+
+struct InconsistentDeclarationInfo {
+ InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
+ DifferingParamsContainer &&DifferingParams)
+ : DeclarationLocation(DeclarationLocation),
+ DifferingParams(std::move(DifferingParams)) {}
+
+ SourceLocation DeclarationLocation;
+ DifferingParamsContainer DifferingParams;
+};
+
+using InconsistentDeclarationsContainer =
+ llvm::SmallVector<InconsistentDeclarationInfo, 2>;
+
+bool checkIfFixItHintIsApplicable(
+ const FunctionDecl *ParameterSourceDeclaration,
+ const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) {
+ // Assumptions with regard to function declarations/definition:
+ // * If both function declaration and definition are seen, assume that
+ // definition is most up-to-date, and use it to generate replacements.
+ // * If only function declarations are seen, there is no easy way to tell
+ // which is up-to-date and which is not, so don't do anything.
+ // TODO: This may be changed later, but for now it seems the reasonable
+ // solution.
+ if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
+ return false;
+
+ // Assumption: if parameter is not referenced in function defintion body, it
+ // may indicate that it's outdated, so don't touch it.
+ if (!SourceParam->isReferenced())
+ return false;
+
+ // In case there is the primary template definition and (possibly several)
+ // template specializations (and each with possibly several redeclarations),
+ // it is not at all clear what to change.
+ if (OriginalDeclaration->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization)
+ return false;
+
+ // Other cases seem OK to allow replacements.
+ return true;
+}
+
+DifferingParamsContainer
+findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OtherDeclaration,
+ const FunctionDecl *OriginalDeclaration) {
+ DifferingParamsContainer DifferingParams;
+
+ auto SourceParamIt = ParameterSourceDeclaration->param_begin();
+ auto OtherParamIt = OtherDeclaration->param_begin();
+
+ while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
+ OtherParamIt != OtherDeclaration->param_end()) {
+ auto SourceParamName = (*SourceParamIt)->getName();
+ auto OtherParamName = (*OtherParamIt)->getName();
+
+ // FIXME: Provide a way to extract commented out parameter name from comment
+ // next to it.
+ if (!SourceParamName.empty() && !OtherParamName.empty() &&
+ SourceParamName != OtherParamName) {
+ SourceRange OtherParamNameRange =
+ DeclarationNameInfo((*OtherParamIt)->getDeclName(),
+ (*OtherParamIt)->getLocation())
+ .getSourceRange();
+
+ bool GenerateFixItHint = checkIfFixItHintIsApplicable(
+ ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
+
+ DifferingParams.emplace_back(SourceParamName, OtherParamName,
+ OtherParamNameRange, GenerateFixItHint);
+ }
+
+ ++SourceParamIt;
+ ++OtherParamIt;
+ }
+
+ return DifferingParams;
+}
+
+InconsistentDeclarationsContainer
+findInconsitentDeclarations(const FunctionDecl *OriginalDeclaration,
+ const FunctionDecl *ParameterSourceDeclaration,
+ SourceManager &SM) {
+ InconsistentDeclarationsContainer InconsistentDeclarations;
+ SourceLocation ParameterSourceLocation =
+ ParameterSourceDeclaration->getLocation();
+
+ for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+ SourceLocation OtherLocation = OtherDeclaration->getLocation();
+ if (OtherLocation != ParameterSourceLocation) { // Skip self.
+ DifferingParamsContainer DifferingParams =
+ findDifferingParamsInDeclaration(ParameterSourceDeclaration,
+ OtherDeclaration,
+ OriginalDeclaration);
+ if (!DifferingParams.empty()) {
+ InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
+ std::move(DifferingParams));
+ }
+ }
+ }
+
+ // Sort in order of appearance in translation unit to generate clear
+ // diagnostics.
+ std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(),
+ [&SM](const InconsistentDeclarationInfo &Info1,
+ const InconsistentDeclarationInfo &Info2) {
+ return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
+ Info2.DeclarationLocation);
+ });
+ return InconsistentDeclarations;
+}
+
+const FunctionDecl *
+getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
+ const FunctionTemplateDecl *PrimaryTemplate =
+ OriginalDeclaration->getPrimaryTemplate();
+ if (PrimaryTemplate != nullptr) {
+ // In case of template specializations, use primary template declaration as
+ // the source of parameter names.
+ return PrimaryTemplate->getTemplatedDecl();
+ }
+
+ // In other cases, try to change to function definition, if available.
+
+ if (OriginalDeclaration->isThisDeclarationADefinition())
+ return OriginalDeclaration;
+
+ for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+ if (OtherDeclaration->isThisDeclarationADefinition()) {
+ return OtherDeclaration;
+ }
+ }
+
+ // No definition found, so return original declaration.
+ return OriginalDeclaration;
+}
+
+std::string joinParameterNames(
+ const DifferingParamsContainer &DifferingParams,
+ llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
+ llvm::SmallVector<char, 40> Buffer;
+ llvm::raw_svector_ostream Str(Buffer);
+ bool First = true;
+ for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+ if (First)
+ First = false;
+ else
+ Str << ", ";
+
+ Str << "'" << ChooseParamName(ParamInfo).str() << "'";
+ }
+ return Str.str().str();
+}
+
+void formatDifferingParamsDiagnostic(
+ InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
+ StringRef OtherDeclarationDescription,
+ const DifferingParamsContainer &DifferingParams) {
+ auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
+ return ParamInfo.OtherName;
+ };
+ auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
+ return ParamInfo.SourceName;
+ };
+
+ auto ParamDiag =
+ Check->diag(Location,
+ "differing parameters are named here: (%0), in %1: (%2)",
+ DiagnosticIDs::Level::Note)
+ << joinParameterNames(DifferingParams, ChooseOtherName)
+ << OtherDeclarationDescription
+ << joinParameterNames(DifferingParams, ChooseSourceName);
+
+ for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+ if (ParamInfo.GenerateFixItHint) {
+ ParamDiag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
+ ParamInfo.SourceName);
+ }
+ }
+}
+
+void formatDiagnosticsForDeclarations(
+ InconsistentDeclarationParameterNameCheck *Check,
+ const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OriginalDeclaration,
+ const InconsistentDeclarationsContainer &InconsistentDeclarations) {
+ Check->diag(
+ OriginalDeclaration->getLocation(),
+ "function %q0 has %1 other declaration%s1 with different parameter names")
+ << OriginalDeclaration
+ << static_cast<int>(InconsistentDeclarations.size());
+ int Count = 1;
+ for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+ InconsistentDeclarations) {
+ Check->diag(InconsistentDeclaration.DeclarationLocation,
+ "the %ordinal0 inconsistent declaration seen here",
+ DiagnosticIDs::Level::Note)
+ << Count;
+
+ formatDifferingParamsDiagnostic(
+ Check, InconsistentDeclaration.DeclarationLocation,
+ "the other declaration", InconsistentDeclaration.DifferingParams);
+
+ ++Count;
+ }
+}
+
+void formatDiagnostics(
+ InconsistentDeclarationParameterNameCheck *Check,
+ const FunctionDecl *ParameterSourceDeclaration,
+ const FunctionDecl *OriginalDeclaration,
+ const InconsistentDeclarationsContainer &InconsistentDeclarations,
+ StringRef FunctionDescription, StringRef ParameterSourceDescription) {
+ for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+ InconsistentDeclarations) {
+ Check->diag(InconsistentDeclaration.DeclarationLocation,
+ "%0 %q1 has a %2 with different parameter names")
+ << FunctionDescription << OriginalDeclaration
+ << ParameterSourceDescription;
+
+ Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
+ DiagnosticIDs::Level::Note)
+ << ParameterSourceDescription;
+
+ formatDifferingParamsDiagnostic(
+ Check, InconsistentDeclaration.DeclarationLocation,
+ ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
+ }
+}
+
+} // anonymous namespace
+
+void InconsistentDeclarationParameterNameCheck::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations())
+ .bind("functionDecl"),
+ this);
+}
+
+void InconsistentDeclarationParameterNameCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *OriginalDeclaration =
+ Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
+
+ if (VisitedDeclarations.count(OriginalDeclaration) > 0)
+ return; // Avoid multiple warnings.
+
+ const FunctionDecl *ParameterSourceDeclaration =
+ getParameterSourceDeclaration(OriginalDeclaration);
+
+ InconsistentDeclarationsContainer InconsistentDeclarations =
+ findInconsitentDeclarations(OriginalDeclaration,
+ ParameterSourceDeclaration,
+ *Result.SourceManager);
+ if (InconsistentDeclarations.empty()) {
+ // Avoid unnecessary further visits.
+ markRedeclarationsAsVisited(OriginalDeclaration);
+ return;
+ }
+
+ if (OriginalDeclaration->getTemplatedKind() ==
+ FunctionDecl::TK_FunctionTemplateSpecialization) {
+ formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+ InconsistentDeclarations,
+ "function template specialization",
+ "primary template declaration");
+ } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
+ formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+ InconsistentDeclarations, "function", "definition");
+ } else {
+ formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
+ OriginalDeclaration,
+ InconsistentDeclarations);
+ }
+
+ markRedeclarationsAsVisited(OriginalDeclaration);
+}
+
+void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
+ const FunctionDecl *OriginalDeclaration) {
+ for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
+ VisitedDeclarations.insert(Redecl);
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===- InconsistentDeclarationParameterNameCheck.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_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/DenseSet.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks for declarations of functions which differ in parameter names.
+///
+/// For detailed documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.html
+///
+class InconsistentDeclarationParameterNameCheck : public ClangTidyCheck {
+public:
+ InconsistentDeclarationParameterNameCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void markRedeclarationsAsVisited(const FunctionDecl *FunctionDeclaration);
+
+ llvm::DenseSet<const FunctionDecl *> VisitedDeclarations;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
--- /dev/null
+//===--- MisleadingIndentationCheck.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 "MisleadingIndentationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+static const IfStmt *getPrecedingIf(const SourceManager &SM,
+ ASTContext *Context, const IfStmt *If) {
+ auto parents = Context->getParents(*If);
+ if (parents.size() != 1)
+ return nullptr;
+ if (const auto *PrecedingIf = parents[0].get<IfStmt>()) {
+ SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
+ if (SM.getExpansionLineNumber(PreviousElseLoc) ==
+ SM.getExpansionLineNumber(If->getIfLoc()))
+ return PrecedingIf;
+ }
+ return nullptr;
+}
+
+void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
+ ASTContext *Context,
+ const IfStmt *If) {
+ SourceLocation IfLoc = If->getIfLoc();
+ SourceLocation ElseLoc = If->getElseLoc();
+
+ if (IfLoc.isMacroID() || ElseLoc.isMacroID())
+ return;
+
+ if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) ==
+ SM.getExpansionLineNumber(ElseLoc))
+ return;
+
+ // Find location of first 'if' in a 'if else if' chain.
+ for (auto PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;
+ PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))
+ IfLoc = PrecedingIf->getIfLoc();
+
+ if (SM.getExpansionColumnNumber(IfLoc) !=
+ SM.getExpansionColumnNumber(ElseLoc))
+ diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
+}
+
+void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
+ const CompoundStmt *CStmt) {
+ const static StringRef StmtNames[] = {"if", "for", "while"};
+ for (unsigned int i = 0; i < CStmt->size() - 1; i++) {
+ const Stmt *CurrentStmt = CStmt->body_begin()[i];
+ const Stmt *Inner = nullptr;
+ int StmtKind = 0;
+
+ if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
+ StmtKind = 0;
+ Inner =
+ CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
+ } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
+ StmtKind = 1;
+ Inner = CurrentFor->getBody();
+ } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
+ StmtKind = 2;
+ Inner = CurrentWhile->getBody();
+ } else {
+ continue;
+ }
+
+ if (isa<CompoundStmt>(Inner))
+ continue;
+
+ SourceLocation InnerLoc = Inner->getLocStart();
+ SourceLocation OuterLoc = CurrentStmt->getLocStart();
+
+ if (SM.getExpansionLineNumber(InnerLoc) ==
+ SM.getExpansionLineNumber(OuterLoc))
+ continue;
+
+ const Stmt *NextStmt = CStmt->body_begin()[i + 1];
+ SourceLocation NextLoc = NextStmt->getLocStart();
+
+ if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID())
+ continue;
+
+ if (SM.getExpansionColumnNumber(InnerLoc) ==
+ SM.getExpansionColumnNumber(NextLoc)) {
+ diag(NextLoc, "misleading indentation: statement is indented too deeply");
+ diag(OuterLoc, "did you mean this line to be inside this '%0'",
+ DiagnosticIDs::Note)
+ << StmtNames[StmtKind];
+ }
+ }
+}
+
+void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
+ Finder->addMatcher(
+ compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
+ .bind("compound"),
+ this);
+}
+
+void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
+ danglingElseCheck(*Result.SourceManager, Result.Context, If);
+
+ if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
+ missingBracesCheck(*Result.SourceManager, CStmt);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MisleadingIndentationCheck.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_READABILITY_MISLEADING_INDENTATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks the code for dangling else, and possible misleading indentations due
+/// to missing braces. Note that this check only works as expected when the tabs
+/// or spaces are used consistently and not mixed.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html
+class MisleadingIndentationCheck : public ClangTidyCheck {
+public:
+ MisleadingIndentationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void danglingElseCheck(const SourceManager &SM, ASTContext *Context,
+ const IfStmt *If);
+ void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt);
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
--- /dev/null
+//===--- MisplacedArrayIndexCheck.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 "MisplacedArrayIndexCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void MisplacedArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(arraySubscriptExpr(hasLHS(hasType(isInteger())),
+ hasRHS(hasType(isAnyPointer())))
+ .bind("expr"),
+ this);
+}
+
+void MisplacedArrayIndexCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ArraySubscriptE =
+ Result.Nodes.getNodeAs<ArraySubscriptExpr>("expr");
+
+ auto Diag = diag(ArraySubscriptE->getLocStart(), "confusing array subscript "
+ "expression, usually the "
+ "index is inside the []");
+
+ // Only try to fixit when LHS and RHS can be swapped directly without changing
+ // the logic.
+ const Expr *RHSE = ArraySubscriptE->getRHS()->IgnoreParenImpCasts();
+ if (!isa<StringLiteral>(RHSE) && !isa<DeclRefExpr>(RHSE) &&
+ !isa<MemberExpr>(RHSE))
+ return;
+
+ const StringRef LText = tooling::fixit::getText(
+ ArraySubscriptE->getLHS()->getSourceRange(), *Result.Context);
+ const StringRef RText = tooling::fixit::getText(
+ ArraySubscriptE->getRHS()->getSourceRange(), *Result.Context);
+
+ Diag << FixItHint::CreateReplacement(
+ ArraySubscriptE->getLHS()->getSourceRange(), RText);
+ Diag << FixItHint::CreateReplacement(
+ ArraySubscriptE->getRHS()->getSourceRange(), LText);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- MisplacedArrayIndexCheck.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_READABILITY_MISPLACED_ARRAY_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Warn about unusual array index syntax (`index[array]` instead of
+/// `array[index]`).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-misplaced-array-index.html
+class MisplacedArrayIndexCheck : public ClangTidyCheck {
+public:
+ MisplacedArrayIndexCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
--- /dev/null
+//===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamedParameterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("decl"), this);
+}
+
+void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
+ const SourceManager &SM = *Result.SourceManager;
+ const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+ SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
+
+ // Ignore implicitly generated members.
+ if (Function->isImplicit())
+ return;
+
+ // Ignore declarations without a definition if we're not dealing with an
+ // overriden method.
+ const FunctionDecl *Definition = nullptr;
+ if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
+ Function->isDeleted()) &&
+ (!isa<CXXMethodDecl>(Function) ||
+ cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
+ return;
+
+ // TODO: Handle overloads.
+ // TODO: We could check that all redeclarations use the same name for
+ // arguments in the same position.
+ for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
+ const ParmVarDecl *Parm = Function->getParamDecl(I);
+ if (Parm->isImplicit())
+ continue;
+ // Look for unnamed parameters.
+ if (!Parm->getName().empty())
+ continue;
+
+ // Don't warn on the dummy argument on post-inc and post-dec operators.
+ if ((Function->getOverloadedOperator() == OO_PlusPlus ||
+ Function->getOverloadedOperator() == OO_MinusMinus) &&
+ Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
+ continue;
+
+ // Sanity check the source locations.
+ if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
+ !SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
+ continue;
+
+ // Skip gmock testing::Unused parameters.
+ if (auto Typedef = Parm->getType()->getAs<clang::TypedefType>())
+ if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
+ continue;
+
+ // Skip std::nullptr_t.
+ if (Parm->getType().getCanonicalType()->isNullPtrType())
+ continue;
+
+ // Look for comments. We explicitly want to allow idioms like
+ // void foo(int /*unused*/)
+ const char *Begin = SM.getCharacterData(Parm->getLocStart());
+ const char *End = SM.getCharacterData(Parm->getLocation());
+ StringRef Data(Begin, End - Begin);
+ if (Data.find("/*") != StringRef::npos)
+ continue;
+
+ UnnamedParams.push_back(std::make_pair(Function, I));
+ }
+
+ // Emit only one warning per function but fixits for all unnamed parameters.
+ if (!UnnamedParams.empty()) {
+ const ParmVarDecl *FirstParm =
+ UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
+ auto D = diag(FirstParm->getLocation(),
+ "all parameters should be named in a function");
+
+ for (auto P : UnnamedParams) {
+ // Fallback to an unused marker.
+ StringRef NewName = "unused";
+
+ // If the method is overridden, try to copy the name from the base method
+ // into the overrider.
+ const auto *M = dyn_cast<CXXMethodDecl>(P.first);
+ if (M && M->size_overridden_methods() > 0) {
+ const ParmVarDecl *OtherParm =
+ (*M->begin_overridden_methods())->getParamDecl(P.second);
+ StringRef Name = OtherParm->getName();
+ if (!Name.empty())
+ NewName = Name;
+ }
+
+ // If the definition has a named parameter use that name.
+ if (Definition) {
+ const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
+ StringRef Name = DefParm->getName();
+ if (!Name.empty())
+ NewName = Name;
+ }
+
+ // Now insert the comment. Note that getLocation() points to the place
+ // where the name would be, this allows us to also get complex cases like
+ // function pointers right.
+ const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
+ D << FixItHint::CreateInsertion(Parm->getLocation(),
+ " /*" + NewName.str() + "*/");
+ }
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NamedParameterCheck.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_READABILITY_NAMEDPARAMETERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find functions with unnamed arguments.
+///
+/// The check implements the following rule originating in the Google C++ Style
+/// Guide:
+///
+/// https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
+///
+/// All parameters should be named, with identical names in the declaration and
+/// implementation.
+///
+/// Corresponding cpplint.py check name: 'readability/function'.
+class NamedParameterCheck : public ClangTidyCheck {
+public:
+ NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
--- /dev/null
+//===--- NamespaceCommentCheck.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 "NamespaceCommentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
+ "namespace( +([a-zA-Z0-9_]+))?\\.? *(\\*/)?$",
+ llvm::Regex::IgnoreCase),
+ ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
+ SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
+
+void NamespaceCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ShortNamespaceLines", ShortNamespaceLines);
+ Options.store(Opts, "SpacesBeforeComments", SpacesBeforeComments);
+}
+
+void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (getLangOpts().CPlusPlus)
+ Finder->addMatcher(namespaceDecl().bind("namespace"), this);
+}
+
+static bool locationsInSameFile(const SourceManager &Sources,
+ SourceLocation Loc1, SourceLocation Loc2) {
+ return Loc1.isFileID() && Loc2.isFileID() &&
+ Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
+}
+
+static std::string getNamespaceComment(const NamespaceDecl *ND,
+ bool InsertLineBreak) {
+ std::string Fix = "// namespace";
+ if (!ND->isAnonymousNamespace())
+ Fix.append(" ").append(ND->getNameAsString());
+ if (InsertLineBreak)
+ Fix.append("\n");
+ return Fix;
+}
+
+void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
+ const SourceManager &Sources = *Result.SourceManager;
+
+ if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc()))
+ return;
+
+ // Don't require closing comments for namespaces spanning less than certain
+ // number of lines.
+ unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart());
+ unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
+ if (EndLine - StartLine + 1 <= ShortNamespaceLines)
+ return;
+
+ // Find next token after the namespace closing brace.
+ SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
+ SourceLocation Loc = AfterRBrace;
+ Token Tok;
+ // Skip whitespace until we find the next token.
+ while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) ||
+ Tok.is(tok::semi)) {
+ Loc = Loc.getLocWithOffset(1);
+ }
+ if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
+ return;
+
+ bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
+ // If we insert a line comment before the token in the same line, we need
+ // to insert a line break.
+ bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
+
+ SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
+ std::string Message = "%0 not terminated with a closing comment";
+
+ // Try to find existing namespace closing comment on the same line.
+ if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
+ StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
+ SmallVector<StringRef, 7> Groups;
+ if (NamespaceCommentPattern.match(Comment, &Groups)) {
+ StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : "";
+ StringRef Anonymous = Groups.size() > 3 ? Groups[3] : "";
+
+ // Check if the namespace in the comment is the same.
+ if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) ||
+ (ND->getNameAsString() == NamespaceNameInComment &&
+ Anonymous.empty())) {
+ // FIXME: Maybe we need a strict mode, where we always fix namespace
+ // comments with different format.
+ return;
+ }
+
+ // Otherwise we need to fix the comment.
+ NeedLineBreak = Comment.startswith("/*");
+ OldCommentRange =
+ SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+ Message =
+ (llvm::Twine(
+ "%0 ends with a comment that refers to a wrong namespace '") +
+ NamespaceNameInComment + "'")
+ .str();
+ } else if (Comment.startswith("//")) {
+ // Assume that this is an unrecognized form of a namespace closing line
+ // comment. Replace it.
+ NeedLineBreak = false;
+ OldCommentRange =
+ SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+ Message = "%0 ends with an unrecognized comment";
+ }
+ // If it's a block comment, just move it to the next line, as it can be
+ // multi-line or there may be other tokens behind it.
+ }
+
+ std::string NamespaceName =
+ ND->isAnonymousNamespace()
+ ? "anonymous namespace"
+ : ("namespace '" + ND->getNameAsString() + "'");
+
+ diag(AfterRBrace, Message)
+ << NamespaceName << FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(OldCommentRange),
+ std::string(SpacesBeforeComments, ' ') +
+ getNamespaceComment(ND, NeedLineBreak));
+ diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note)
+ << NamespaceName;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NamespaceCommentCheck.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_READABILITY_NAMESPACECOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks that long namespaces have a closing comment.
+///
+/// http://llvm.org/docs/CodingStandards.html#namespace-indentation
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+class NamespaceCommentCheck : public ClangTidyCheck {
+public:
+ NamespaceCommentCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+ llvm::Regex NamespaceCommentPattern;
+ const unsigned ShortNamespaceLines;
+ const unsigned SpacesBeforeComments;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
--- /dev/null
+//===--- NonConstParameterCheck.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 "NonConstParameterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
+ // Add parameters to Parameters.
+ Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this);
+
+ // C++ constructor.
+ Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
+
+ // Track unused parameters, there is Wunused-parameter about unused
+ // parameters.
+ Finder->addMatcher(declRefExpr().bind("Ref"), this);
+
+ // Analyse parameter usage in function.
+ Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName("++"),
+ hasOperatorName("--"))),
+ binaryOperator(), callExpr(), returnStmt(),
+ cxxConstructExpr()))
+ .bind("Mark"),
+ this);
+ Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
+}
+
+void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>("Parm")) {
+ if (const DeclContext *D = Parm->getParentFunctionOrMethod()) {
+ if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
+ if (M->isVirtual() || M->size_overridden_methods() != 0)
+ return;
+ }
+ }
+ addParm(Parm);
+ } else if (const auto *Ctor =
+ Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) {
+ for (const auto *Parm : Ctor->parameters())
+ addParm(Parm);
+ for (const auto *Init : Ctor->inits())
+ markCanNotBeConst(Init->getInit(), true);
+ } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) {
+ setReferenced(Ref);
+ } else if (const auto *S = Result.Nodes.getNodeAs<Stmt>("Mark")) {
+ if (const auto *B = dyn_cast<BinaryOperator>(S)) {
+ if (B->isAssignmentOp())
+ markCanNotBeConst(B, false);
+ } else if (const auto *CE = dyn_cast<CallExpr>(S)) {
+ // Typically, if a parameter is const then it is fine to make the data
+ // const. But sometimes the data is written even though the parameter
+ // is const. Mark all data passed by address to the function.
+ for (const auto *Arg : CE->arguments()) {
+ markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+ }
+
+ // Data passed by nonconst reference should not be made const.
+ if (const FunctionDecl *FD = CE->getDirectCallee()) {
+ unsigned ArgNr = 0U;
+ for (const auto *Par : FD->parameters()) {
+ if (ArgNr >= CE->getNumArgs())
+ break;
+ const Expr *Arg = CE->getArg(ArgNr++);
+ // Is this a non constant reference parameter?
+ const Type *ParType = Par->getType().getTypePtr();
+ if (!ParType->isReferenceType() || Par->getType().isConstQualified())
+ continue;
+ markCanNotBeConst(Arg->IgnoreParenCasts(), false);
+ }
+ }
+ } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
+ for (const auto *Arg : CE->arguments()) {
+ markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+ }
+ } else if (const auto *R = dyn_cast<ReturnStmt>(S)) {
+ markCanNotBeConst(R->getRetValue(), true);
+ } else if (const auto *U = dyn_cast<UnaryOperator>(S)) {
+ markCanNotBeConst(U, true);
+ }
+ } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
+ const QualType T = VD->getType();
+ if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
+ T->isArrayType())
+ markCanNotBeConst(VD->getInit(), true);
+ }
+}
+
+void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
+ // Only add nonconst integer/float pointer parameters.
+ const QualType T = Parm->getType();
+ if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
+ !(T->getPointeeType()->isIntegerType() ||
+ T->getPointeeType()->isFloatingType()))
+ return;
+
+ if (Parameters.find(Parm) != Parameters.end())
+ return;
+
+ ParmInfo PI;
+ PI.IsReferenced = false;
+ PI.CanBeConst = true;
+ Parameters[Parm] = PI;
+}
+
+void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
+ auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
+ if (It != Parameters.end())
+ It->second.IsReferenced = true;
+}
+
+void NonConstParameterCheck::onEndOfTranslationUnit() {
+ diagnoseNonConstParameters();
+}
+
+void NonConstParameterCheck::diagnoseNonConstParameters() {
+ for (const auto &It : Parameters) {
+ const ParmVarDecl *Par = It.first;
+ const ParmInfo &ParamInfo = It.second;
+
+ // Unused parameter => there are other warnings about this.
+ if (!ParamInfo.IsReferenced)
+ continue;
+
+ // Parameter can't be const.
+ if (!ParamInfo.CanBeConst)
+ continue;
+
+ diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
+ << Par->getName()
+ << FixItHint::CreateInsertion(Par->getLocStart(), "const ");
+ }
+}
+
+void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
+ bool CanNotBeConst) {
+ if (!E)
+ return;
+
+ if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
+ // If expression is const then ignore usage.
+ const QualType T = Cast->getType();
+ if (T->isPointerType() && T->getPointeeType().isConstQualified())
+ return;
+ }
+
+ E = E->IgnoreParenCasts();
+
+ if (const auto *B = dyn_cast<BinaryOperator>(E)) {
+ if (B->isAdditiveOp()) {
+ // p + 2
+ markCanNotBeConst(B->getLHS(), CanNotBeConst);
+ markCanNotBeConst(B->getRHS(), CanNotBeConst);
+ } else if (B->isAssignmentOp()) {
+ markCanNotBeConst(B->getLHS(), false);
+
+ // If LHS is not const then RHS can't be const.
+ const QualType T = B->getLHS()->getType();
+ if (T->isPointerType() && !T->getPointeeType().isConstQualified())
+ markCanNotBeConst(B->getRHS(), true);
+ }
+ } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
+ markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
+ markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
+ } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
+ if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
+ U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
+ if (const auto *SubU =
+ dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
+ markCanNotBeConst(SubU->getSubExpr(), true);
+ markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+ } else if (U->getOpcode() == UO_Deref) {
+ if (!CanNotBeConst)
+ markCanNotBeConst(U->getSubExpr(), true);
+ } else {
+ markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+ }
+ } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
+ markCanNotBeConst(A->getBase(), true);
+ } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
+ markCanNotBeConst(CLE->getInitializer(), true);
+ } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
+ for (const auto *Arg : Constr->arguments()) {
+ if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
+ markCanNotBeConst(cast<Expr>(M->getTemporary()), CanNotBeConst);
+ }
+ } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
+ for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
+ markCanNotBeConst(ILE->getInit(I), true);
+ } else if (CanNotBeConst) {
+ // Referencing parameter.
+ if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
+ auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
+ if (It != Parameters.end())
+ It->second.CanBeConst = false;
+ }
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- NonConstParameterCheck.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_READABILITY_NON_CONST_PARAMETER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Warn when a pointer function parameter can be const.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-non-const-parameter.html
+class NonConstParameterCheck : public ClangTidyCheck {
+public:
+ NonConstParameterCheck(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;
+
+private:
+ /// Parameter info.
+ struct ParmInfo {
+ /// Is function parameter referenced?
+ bool IsReferenced;
+
+ /// Can function parameter be const?
+ bool CanBeConst;
+ };
+
+ /// Track all nonconst integer/float parameters.
+ std::map<const ParmVarDecl *, ParmInfo> Parameters;
+
+ /// Add function parameter.
+ void addParm(const ParmVarDecl *Parm);
+
+ /// Set IsReferenced.
+ void setReferenced(const DeclRefExpr *Ref);
+
+ /// Set CanNotBeConst.
+ /// Visits sub expressions recursively. If a DeclRefExpr is found
+ /// and CanNotBeConst is true the Parameter is marked as not-const.
+ /// The CanNotBeConst is updated as sub expressions are visited.
+ void markCanNotBeConst(const Expr *E, bool CanNotBeConst);
+
+ /// Diagnose non const parameters.
+ void diagnoseNonConstParameters();
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
--- /dev/null
+//===--- ReadabilityTidyModule.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 "AvoidConstParamsInDecls.h"
+#include "BracesAroundStatementsCheck.h"
+#include "ContainerSizeEmptyCheck.h"
+#include "DeleteNullPointerCheck.h"
+#include "DeletedDefaultCheck.h"
+#include "ElseAfterReturnCheck.h"
+#include "FunctionSizeCheck.h"
+#include "IdentifierNamingCheck.h"
+#include "ImplicitBoolCastCheck.h"
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "MisleadingIndentationCheck.h"
+#include "MisplacedArrayIndexCheck.h"
+#include "NamedParameterCheck.h"
+#include "NonConstParameterCheck.h"
+#include "RedundantControlFlowCheck.h"
+#include "RedundantDeclarationCheck.h"
+#include "RedundantFunctionPtrDereferenceCheck.h"
+#include "RedundantMemberInitCheck.h"
+#include "RedundantSmartptrGetCheck.h"
+#include "RedundantStringCStrCheck.h"
+#include "RedundantStringInitCheck.h"
+#include "SimplifyBooleanExprCheck.h"
+#include "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "UniqueptrDeleteReleaseCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+class ReadabilityModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AvoidConstParamsInDecls>(
+ "readability-avoid-const-params-in-decls");
+ CheckFactories.registerCheck<BracesAroundStatementsCheck>(
+ "readability-braces-around-statements");
+ CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
+ "readability-container-size-empty");
+ CheckFactories.registerCheck<DeleteNullPointerCheck>(
+ "readability-delete-null-pointer");
+ CheckFactories.registerCheck<DeletedDefaultCheck>(
+ "readability-deleted-default");
+ CheckFactories.registerCheck<ElseAfterReturnCheck>(
+ "readability-else-after-return");
+ CheckFactories.registerCheck<FunctionSizeCheck>(
+ "readability-function-size");
+ CheckFactories.registerCheck<IdentifierNamingCheck>(
+ "readability-identifier-naming");
+ CheckFactories.registerCheck<ImplicitBoolCastCheck>(
+ "readability-implicit-bool-cast");
+ CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
+ "readability-inconsistent-declaration-parameter-name");
+ CheckFactories.registerCheck<MisleadingIndentationCheck>(
+ "readability-misleading-indentation");
+ CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
+ "readability-misplaced-array-index");
+ CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>(
+ "readability-redundant-function-ptr-dereference");
+ CheckFactories.registerCheck<RedundantMemberInitCheck>(
+ "readability-redundant-member-init");
+ CheckFactories.registerCheck<StaticDefinitionInAnonymousNamespaceCheck>(
+ "readability-static-definition-in-anonymous-namespace");
+ CheckFactories.registerCheck<readability::NamedParameterCheck>(
+ "readability-named-parameter");
+ CheckFactories.registerCheck<NonConstParameterCheck>(
+ "readability-non-const-parameter");
+ CheckFactories.registerCheck<RedundantControlFlowCheck>(
+ "readability-redundant-control-flow");
+ CheckFactories.registerCheck<RedundantDeclarationCheck>(
+ "readability-redundant-declaration");
+ CheckFactories.registerCheck<RedundantSmartptrGetCheck>(
+ "readability-redundant-smartptr-get");
+ CheckFactories.registerCheck<RedundantStringCStrCheck>(
+ "readability-redundant-string-cstr");
+ CheckFactories.registerCheck<RedundantStringInitCheck>(
+ "readability-redundant-string-init");
+ CheckFactories.registerCheck<SimplifyBooleanExprCheck>(
+ "readability-simplify-boolean-expr");
+ CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
+ "readability-uniqueptr-delete-release");
+ }
+};
+
+// Register the ReadabilityModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<ReadabilityModule>
+ X("readability-module", "Adds readability-related checks.");
+
+} // namespace readability
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the ReadabilityModule.
+volatile int ReadabilityModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantControlFlowCheck.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 "RedundantControlFlowCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+const char *const RedundantReturnDiag = "redundant return statement at the end "
+ "of a function with a void return type";
+const char *const RedundantContinueDiag = "redundant continue statement at the "
+ "end of loop statement";
+
+bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) {
+ return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+} // namespace
+
+void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ functionDecl(
+ isDefinition(), returns(voidType()),
+ has(compoundStmt(hasAnySubstatement(returnStmt(unless(has(expr())))))
+ .bind("return"))),
+ this);
+ auto CompoundContinue =
+ has(compoundStmt(hasAnySubstatement(continueStmt())).bind("continue"));
+ Finder->addMatcher(
+ stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt()),
+ CompoundContinue),
+ this);
+}
+
+void RedundantControlFlowCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Return = Result.Nodes.getNodeAs<CompoundStmt>("return"))
+ checkRedundantReturn(Result, Return);
+ else if (const auto *Continue =
+ Result.Nodes.getNodeAs<CompoundStmt>("continue"))
+ checkRedundantContinue(Result, Continue);
+}
+
+void RedundantControlFlowCheck::checkRedundantReturn(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+ CompoundStmt::const_reverse_body_iterator last = Block->body_rbegin();
+ if (const auto *Return = dyn_cast<ReturnStmt>(*last))
+ issueDiagnostic(Result, Block, Return->getSourceRange(),
+ RedundantReturnDiag);
+}
+
+void RedundantControlFlowCheck::checkRedundantContinue(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+ CompoundStmt::const_reverse_body_iterator last = Block->body_rbegin();
+ if (const auto *Continue = dyn_cast<ContinueStmt>(*last))
+ issueDiagnostic(Result, Block, Continue->getSourceRange(),
+ RedundantContinueDiag);
+}
+
+void RedundantControlFlowCheck::issueDiagnostic(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *const Block,
+ const SourceRange &StmtRange, const char *const Diag) {
+ SourceManager &SM = *Result.SourceManager;
+ if (isLocationInMacroExpansion(SM, StmtRange.getBegin()))
+ return;
+
+ CompoundStmt::const_reverse_body_iterator Previous = ++Block->body_rbegin();
+ SourceLocation Start;
+ if (Previous != Block->body_rend())
+ Start = Lexer::findLocationAfterToken(
+ dyn_cast<Stmt>(*Previous)->getLocEnd(), tok::semi, SM, getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true);
+ if (!Start.isValid())
+ Start = StmtRange.getBegin();
+ auto RemovedRange = CharSourceRange::getCharRange(
+ Start, Lexer::findLocationAfterToken(
+ StmtRange.getEnd(), tok::semi, SM, getLangOpts(),
+ /*SkipTrailingWhitespaceAndNewLine=*/true));
+
+ diag(StmtRange.getBegin(), Diag) << FixItHint::CreateRemoval(RemovedRange);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantControlFlowCheck.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_READABILITY_REDUNDANT_CONTROL_FLOW_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Eliminates redundant `return` statements at the end of a function that
+/// returns `void`.
+///
+/// Eliminates redundant `continue` statements at the end of a loop body.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-control-flow.html
+class RedundantControlFlowCheck : public ClangTidyCheck {
+public:
+ RedundantControlFlowCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void
+ checkRedundantReturn(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block);
+
+ void
+ checkRedundantContinue(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block);
+
+ void issueDiagnostic(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Block, const SourceRange &StmtRange,
+ const char *Diag);
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
--- /dev/null
+//===--- RedundantDeclarationCheck.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 "RedundantDeclarationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ namedDecl(
+ anyOf(varDecl(unless(isDefinition())),
+ functionDecl(unless(anyOf(isDefinition(), isDefaulted())))))
+ .bind("Decl"),
+ this);
+}
+
+void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *D = Result.Nodes.getNodeAs<NamedDecl>("Decl");
+ const auto *Prev = D->getPreviousDecl();
+ if (!Prev)
+ return;
+ if (!Prev->getLocation().isValid())
+ return;
+ if (Prev->getLocation() == D->getLocation())
+ return;
+
+ const SourceManager &SM = *Result.SourceManager;
+
+ const bool DifferentHeaders =
+ !SM.isInMainFile(D->getLocation()) &&
+ !SM.isWrittenInSameFile(Prev->getLocation(), D->getLocation());
+
+ bool MultiVar = false;
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ // Is this a multivariable declaration?
+ for (const auto Other : VD->getDeclContext()->decls()) {
+ if (Other != D && Other->getLocStart() == VD->getLocStart()) {
+ MultiVar = true;
+ break;
+ }
+ }
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ D->getSourceRange().getEnd(), 0, SM, Result.Context->getLangOpts());
+ {
+ auto Diag = diag(D->getLocation(), "redundant %0 declaration") << D;
+ if (!MultiVar && !DifferentHeaders)
+ Diag << FixItHint::CreateRemoval(
+ SourceRange(D->getSourceRange().getBegin(), EndLoc));
+ }
+ diag(Prev->getLocation(), "previously declared here", DiagnosticIDs::Note);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantDeclarationCheck.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_READABILITY_REDUNDANT_DECLARATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find redundant variable declarations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-declaration.html
+class RedundantDeclarationCheck : public ClangTidyCheck {
+public:
+ RedundantDeclarationCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
--- /dev/null
+//===--- RedundantFunctionPtrDereferenceCheck.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 "RedundantFunctionPtrDereferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantFunctionPtrDereferenceCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(unaryOperator(hasOperatorName("*"),
+ has(implicitCastExpr(
+ hasCastKind(CK_FunctionToPointerDecay))))
+ .bind("op"),
+ this);
+}
+
+void RedundantFunctionPtrDereferenceCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Operator = Result.Nodes.getNodeAs<UnaryOperator>("op");
+ diag(Operator->getOperatorLoc(),
+ "redundant repeated dereference of function pointer")
+ << FixItHint::CreateRemoval(Operator->getOperatorLoc());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantFunctionPtrDereferenceCheck.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_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Eliminate redundant dereferences of a function pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-function-ptr-dereference.html
+class RedundantFunctionPtrDereferenceCheck : public ClangTidyCheck {
+public:
+ RedundantFunctionPtrDereferenceCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
--- /dev/null
+//===--- RedundantMemberInitCheck.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 "RedundantMemberInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto Construct =
+ cxxConstructExpr(
+ hasDeclaration(cxxConstructorDecl(hasParent(
+ cxxRecordDecl(unless(isTriviallyDefaultConstructible()))))))
+ .bind("construct");
+
+ Finder->addMatcher(
+ cxxConstructorDecl(
+ unless(isDelegatingConstructor()),
+ ofClass(unless(
+ anyOf(isUnion(), ast_matchers::isTemplateInstantiation()))),
+ forEachConstructorInitializer(
+ cxxCtorInitializer(isWritten(),
+ withInitializer(ignoringImplicit(Construct)),
+ unless(forField(hasType(isConstQualified()))))
+ .bind("init"))),
+ this);
+}
+
+void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
+ const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
+
+ if (Construct->getNumArgs() == 0 ||
+ Construct->getArg(0)->isDefaultArgument()) {
+ if (Init->isAnyMemberInitializer()) {
+ diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
+ << Init->getMember()
+ << FixItHint::CreateRemoval(Init->getSourceRange());
+ } else {
+ diag(Init->getSourceLocation(),
+ "initializer for base class %0 is redundant")
+ << Construct->getType()
+ << FixItHint::CreateRemoval(Init->getSourceRange());
+ }
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantMemberInitCheck.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_READABILITY_REDUNDANT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds member initializations that are unnecessary because the same default
+/// constructor would be called if they were not present.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-member-init.html
+class RedundantMemberInitCheck : public ClangTidyCheck {
+public:
+ RedundantMemberInitCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
--- /dev/null
+//===--- RedundantSmartptrGetCheck.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 "RedundantSmartptrGetCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+internal::Matcher<Expr> callToGet(const internal::Matcher<Decl> &OnClass) {
+ return cxxMemberCallExpr(
+ on(expr(anyOf(hasType(OnClass),
+ hasType(qualType(
+ pointsTo(decl(OnClass).bind("ptr_to_ptr"))))))
+ .bind("smart_pointer")),
+ unless(callee(memberExpr(hasObjectExpression(cxxThisExpr())))),
+ callee(cxxMethodDecl(
+ hasName("get"),
+ returns(qualType(pointsTo(type().bind("getType")))))))
+ .bind("redundant_get");
+}
+
+void registerMatchersForGetArrowStart(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ const auto QuacksLikeASmartptr = recordDecl(
+ recordDecl().bind("duck_typing"),
+ has(cxxMethodDecl(hasName("operator->"),
+ returns(qualType(pointsTo(type().bind("op->Type")))))),
+ has(cxxMethodDecl(hasName("operator*"), returns(qualType(references(
+ type().bind("op*Type")))))));
+
+ // Catch 'ptr.get()->Foo()'
+ Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
+ hasObjectExpression(ignoringImpCasts(
+ callToGet(QuacksLikeASmartptr)))),
+ Callback);
+
+ // Catch '*ptr.get()' or '*ptr->get()'
+ Finder->addMatcher(
+ unaryOperator(hasOperatorName("*"),
+ hasUnaryOperand(callToGet(QuacksLikeASmartptr))),
+ Callback);
+}
+
+void registerMatchersForGetEquals(MatchFinder *Finder,
+ MatchFinder::MatchCallback *Callback) {
+ // This one is harder to do with duck typing.
+ // The operator==/!= that we are looking for might be member or non-member,
+ // might be on global namespace or found by ADL, might be a template, etc.
+ // For now, lets keep a list of known standard types.
+
+ const auto IsAKnownSmartptr =
+ recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
+
+ // Matches against nullptr.
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+ hasEitherOperand(ignoringImpCasts(
+ anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),
+ integerLiteral(equals(0))))),
+ hasEitherOperand(callToGet(IsAKnownSmartptr))),
+ Callback);
+
+ // Matches against if(ptr.get())
+ Finder->addMatcher(
+ ifStmt(hasCondition(ignoringImpCasts(callToGet(IsAKnownSmartptr)))),
+ Callback);
+
+ // FIXME: Match and fix if (l.get() == r.get()).
+}
+
+} // namespace
+
+void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ registerMatchersForGetArrowStart(Finder, this);
+ registerMatchersForGetEquals(Finder, this);
+}
+
+namespace {
+bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
+ if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
+ return true;
+ // Verify that the types match.
+ // We can't do this on the matcher because the type nodes can be different,
+ // even though they represent the same type. This difference comes from how
+ // the type is referenced (eg. through a typedef, a type trait, etc).
+ const Type *OpArrowType =
+ Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
+ const Type *OpStarType =
+ Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
+ const Type *GetType =
+ Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
+ return OpArrowType == OpStarType && OpArrowType == GetType;
+}
+} // namespace
+
+void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
+ if (!allReturnTypesMatch(Result))
+ return;
+
+ bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
+ bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
+ const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
+ const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
+
+ if (IsPtrToPtr && IsMemberExpr) {
+ // Ignore this case (eg. Foo->get()->DoSomething());
+ return;
+ }
+
+ StringRef SmartptrText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
+ // Replace foo->get() with *foo, and foo.get() with foo.
+ std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
+ diag(GetCall->getLocStart(), "redundant get() call on smart pointer")
+ << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantSmartptrGetCheck.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_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find and remove redundant calls to smart pointer's `.get()` method.
+///
+/// Examples:
+///
+/// \code
+/// ptr.get()->Foo() ==> ptr->Foo()
+/// *ptr.get() ==> *ptr
+/// *ptr->get() ==> **ptr
+/// \endcode
+class RedundantSmartptrGetCheck : public ClangTidyCheck {
+public:
+ RedundantSmartptrGetCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
--- /dev/null
+//===- RedundantStringCStrCheck.cpp - Check for redundant c_str calls -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a check for redundant calls of c_str() on strings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringCStrCheck.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+template <typename T>
+StringRef getText(const ast_matchers::MatchFinder::MatchResult &Result,
+ T const &Node) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Node.getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+}
+
+// Return true if expr needs to be put in parens when it is an argument of a
+// prefix unary operator, e.g. when it is a binary or ternary operator
+// syntactically.
+bool needParensAfterUnaryOperator(const Expr &ExprNode) {
+ if (isa<clang::BinaryOperator>(&ExprNode) ||
+ isa<clang::ConditionalOperator>(&ExprNode)) {
+ return true;
+ }
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
+ return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
+ Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
+ Op->getOperator() != OO_Subscript;
+ }
+ return false;
+}
+
+// Format a pointer to an expression: prefix with '*' but simplify
+// when it already begins with '&'. Return empty string on failure.
+std::string
+formatDereference(const ast_matchers::MatchFinder::MatchResult &Result,
+ const Expr &ExprNode) {
+ if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
+ if (Op->getOpcode() == UO_AddrOf) {
+ // Strip leading '&'.
+ return getText(Result, *Op->getSubExpr()->IgnoreParens());
+ }
+ }
+ StringRef Text = getText(Result, ExprNode);
+ if (Text.empty())
+ return std::string();
+ // Add leading '*'.
+ if (needParensAfterUnaryOperator(ExprNode)) {
+ return (llvm::Twine("*(") + Text + ")").str();
+ }
+ return (llvm::Twine("*") + Text).str();
+}
+
+} // end namespace
+
+void RedundantStringCStrCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ // Only register the matchers for C++; the functionality currently does not
+ // provide any benefit to other languages, despite being benign.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Match expressions of type 'string' or 'string*'.
+ const auto StringDecl = cxxRecordDecl(hasName("::std::basic_string"));
+ const auto StringExpr =
+ expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(anyOf(
+ cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+ cxxConstructExpr(
+ argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ // If present, the second argument is the alloc object which must not
+ // be present explicitly.
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Match a call to the string 'c_str()' method.
+ const auto StringCStrCallExpr =
+ cxxMemberCallExpr(on(StringExpr.bind("arg")),
+ callee(memberExpr().bind("member")),
+ callee(cxxMethodDecl(hasAnyName("c_str", "data"))))
+ .bind("call");
+
+ // Detect redundant 'c_str()' calls through a string constructor.
+ Finder->addMatcher(cxxConstructExpr(StringConstructorExpr,
+ hasArgument(0, StringCStrCallExpr)),
+ this);
+
+ // Detect: 's == str.c_str()' -> 's == str'
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ anyOf(
+ hasOverloadedOperatorName("<"), hasOverloadedOperatorName(">"),
+ hasOverloadedOperatorName(">="), hasOverloadedOperatorName("<="),
+ hasOverloadedOperatorName("!="), hasOverloadedOperatorName("=="),
+ hasOverloadedOperatorName("+")),
+ anyOf(allOf(hasArgument(0, StringExpr),
+ hasArgument(1, StringCStrCallExpr)),
+ allOf(hasArgument(0, StringCStrCallExpr),
+ hasArgument(1, StringExpr)))),
+ this);
+
+ // Detect: 'dst += str.c_str()' -> 'dst += str'
+ // Detect: 's = str.c_str()' -> 's = str'
+ Finder->addMatcher(cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("="),
+ hasOverloadedOperatorName("+=")),
+ hasArgument(0, StringExpr),
+ hasArgument(1, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.append(str.c_str())' -> 'dst.append(str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName(
+ "append", "assign", "compare")))),
+ argumentCountIs(1), hasArgument(0, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.compare(p, n, str.c_str())' -> 'dst.compare(p, n, str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasName("compare")))),
+ argumentCountIs(3), hasArgument(2, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.find(str.c_str())' -> 'dst.find(str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasAnyName(
+ "find", "find_first_not_of", "find_first_of",
+ "find_last_not_of", "find_last_of", "rfind")))),
+ anyOf(argumentCountIs(1), argumentCountIs(2)),
+ hasArgument(0, StringCStrCallExpr)),
+ this);
+
+ // Detect: 'dst.insert(pos, str.c_str())' -> 'dst.insert(pos, str)'
+ Finder->addMatcher(
+ cxxMemberCallExpr(on(StringExpr),
+ callee(decl(cxxMethodDecl(hasName("insert")))),
+ argumentCountIs(2), hasArgument(1, StringCStrCallExpr)),
+ this);
+
+ // Detect redundant 'c_str()' calls through a StringRef constructor.
+ Finder->addMatcher(
+ cxxConstructExpr(
+ // Implicit constructors of these classes are overloaded
+ // wrt. string types and they internally make a StringRef
+ // referring to the argument. Passing a string directly to
+ // them is preferred to passing a char pointer.
+ hasDeclaration(cxxMethodDecl(hasAnyName(
+ "::llvm::StringRef::StringRef", "::llvm::Twine::Twine"))),
+ argumentCountIs(1),
+ // The only argument must have the form x.c_str() or p->c_str()
+ // where the method is string::c_str(). StringRef also has
+ // a constructor from string which is more efficient (avoids
+ // strlen), so we can construct StringRef from the string
+ // directly.
+ hasArgument(0, StringCStrCallExpr)),
+ this);
+}
+
+void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
+ const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
+ bool Arrow = Member->isArrow();
+ // Replace the "call" node with the "arg" node, prefixed with '*'
+ // if the call was using '->' rather than '.'.
+ std::string ArgText =
+ Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str();
+ if (ArgText.empty())
+ return;
+
+ diag(Call->getLocStart(), "redundant call to %0")
+ << Member->getMemberDecl()
+ << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- RedundantStringCStrCheck.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_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds unnecessary calls to `std::string::c_str()`.
+class RedundantStringCStrCheck : public ClangTidyCheck {
+public:
+ RedundantStringCStrCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
--- /dev/null
+//===- RedundantStringInitCheck.cpp - clang-tidy ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(anyOf(
+ cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+ // If present, the second argument is the alloc object which must not
+ // be present explicitly.
+ cxxConstructExpr(argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Match a string constructor expression with an empty string literal.
+ const auto EmptyStringCtorExpr = cxxConstructExpr(
+ StringConstructorExpr,
+ hasArgument(0, ignoringParenImpCasts(stringLiteral(hasSize(0)))));
+
+ const auto EmptyStringCtorExprWithTemporaries =
+ cxxConstructExpr(StringConstructorExpr,
+ hasArgument(0, ignoringImplicit(EmptyStringCtorExpr)));
+
+ // Match a variable declaration with an empty string literal as initializer.
+ // Examples:
+ // string foo = "";
+ // string bar("");
+ Finder->addMatcher(
+ namedDecl(
+ varDecl(hasType(cxxRecordDecl(hasName("basic_string"))),
+ hasInitializer(expr(ignoringImplicit(anyOf(
+ EmptyStringCtorExpr,
+ EmptyStringCtorExprWithTemporaries)))
+ .bind("expr"))),
+ unless(parmVarDecl()))
+ .bind("decl"),
+ this);
+}
+
+void RedundantStringInitCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *CtorExpr = Result.Nodes.getNodeAs<Expr>("expr");
+ const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl");
+ diag(CtorExpr->getExprLoc(), "redundant string initialization")
+ << FixItHint::CreateReplacement(CtorExpr->getSourceRange(),
+ Decl->getName());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===- RedundantStringInitCheck.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_READABILITY_REDUNDANT_STRING_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds unnecessary string initializations.
+class RedundantStringInitCheck : public ClangTidyCheck {
+public:
+ RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
--- /dev/null
+//===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SimplifyBooleanExprCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Lex/Lexer.h"
+
+#include <cassert>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
+ return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
+ *Result.SourceManager,
+ Result.Context->getLangOpts());
+}
+
+template <typename T>
+StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
+ return getText(Result, Node.getSourceRange());
+}
+
+const char ConditionThenStmtId[] = "if-bool-yields-then";
+const char ConditionElseStmtId[] = "if-bool-yields-else";
+const char TernaryId[] = "ternary-bool-yields-condition";
+const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
+const char IfReturnsBoolId[] = "if-return";
+const char IfReturnsNotBoolId[] = "if-not-return";
+const char ThenLiteralId[] = "then-literal";
+const char IfAssignVariableId[] = "if-assign-lvalue";
+const char IfAssignLocId[] = "if-assign-loc";
+const char IfAssignBoolId[] = "if-assign";
+const char IfAssignNotBoolId[] = "if-assign-not";
+const char IfAssignObjId[] = "if-assign-obj";
+const char CompoundReturnId[] = "compound-return";
+const char CompoundBoolId[] = "compound-bool";
+const char CompoundNotBoolId[] = "compound-bool-not";
+
+const char IfStmtId[] = "if";
+
+const char SimplifyOperatorDiagnostic[] =
+ "redundant boolean literal supplied to boolean operator";
+const char SimplifyConditionDiagnostic[] =
+ "redundant boolean literal in if statement condition";
+const char SimplifyConditionalReturnDiagnostic[] =
+ "redundant boolean literal in conditional return statement";
+
+const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
+ StringRef Id) {
+ const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
+ return (Literal &&
+ Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
+ ? nullptr
+ : Literal;
+}
+
+internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
+ auto SimpleReturnsBool =
+ returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
+ .bind("returns-bool");
+ return anyOf(SimpleReturnsBool,
+ compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
+}
+
+bool needsParensAfterUnaryNegation(const Expr *E) {
+ E = E->IgnoreImpCasts();
+ if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
+ return true;
+
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
+ return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
+ Op->getOperator() != OO_Subscript;
+
+ return false;
+}
+
+std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
+ {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
+
+StringRef negatedOperator(const BinaryOperator *BinOp) {
+ const BinaryOperatorKind Opcode = BinOp->getOpcode();
+ for (auto NegatableOp : Opposites) {
+ if (Opcode == NegatableOp.first)
+ return BinOp->getOpcodeStr(NegatableOp.second);
+ if (Opcode == NegatableOp.second)
+ return BinOp->getOpcodeStr(NegatableOp.first);
+ }
+ return StringRef();
+}
+
+std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
+ {OO_EqualEqual, "=="}, {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
+ {OO_GreaterEqual, ">="}, {OO_Greater, ">"}, {OO_LessEqual, "<="}};
+
+StringRef getOperatorName(OverloadedOperatorKind OpKind) {
+ for (auto Name : OperatorNames) {
+ if (Name.first == OpKind)
+ return Name.second;
+ }
+
+ return StringRef();
+}
+
+std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
+ {{OO_EqualEqual, OO_ExclaimEqual},
+ {OO_Less, OO_GreaterEqual},
+ {OO_Greater, OO_LessEqual}};
+
+StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
+ const OverloadedOperatorKind Opcode = OpCall->getOperator();
+ for (auto NegatableOp : OppositeOverloads) {
+ if (Opcode == NegatableOp.first)
+ return getOperatorName(NegatableOp.second);
+ if (Opcode == NegatableOp.second)
+ return getOperatorName(NegatableOp.first);
+ }
+ return StringRef();
+}
+
+std::string asBool(StringRef text, bool NeedsStaticCast) {
+ if (NeedsStaticCast)
+ return ("static_cast<bool>(" + text + ")").str();
+
+ return text;
+}
+
+bool needsNullPtrComparison(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+ return ImpCast->getCastKind() == CK_PointerToBoolean ||
+ ImpCast->getCastKind() == CK_MemberPointerToBoolean;
+
+ return false;
+}
+
+bool needsZeroComparison(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+ return ImpCast->getCastKind() == CK_IntegralToBoolean;
+
+ return false;
+}
+
+bool needsStaticCast(const Expr *E) {
+ if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
+ if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
+ ImpCast->getSubExpr()->getType()->isBooleanType()) {
+ if (const auto *MemCall =
+ dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
+ if (const auto *MemDecl =
+ dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
+ if (MemDecl->isExplicit())
+ return true;
+ }
+ }
+ }
+ }
+
+ E = E->IgnoreImpCasts();
+ return !E->getType()->isBooleanType();
+}
+
+std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
+ const Expr *E, bool Negated,
+ const char *Constant) {
+ E = E->IgnoreImpCasts();
+ const std::string ExprText =
+ (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
+ : getText(Result, *E))
+ .str();
+ return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
+}
+
+std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
+ const Expr *E, bool Negated) {
+ const char *NullPtr =
+ Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
+ return compareExpressionToConstant(Result, E, Negated, NullPtr);
+}
+
+std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
+ const Expr *E, bool Negated) {
+ return compareExpressionToConstant(Result, E, Negated, "0");
+}
+
+std::string replacementExpression(const MatchFinder::MatchResult &Result,
+ bool Negated, const Expr *E) {
+ E = E->ignoreParenBaseCasts();
+ const bool NeedsStaticCast = needsStaticCast(E);
+ if (Negated) {
+ if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+ if (UnOp->getOpcode() == UO_LNot) {
+ if (needsNullPtrComparison(UnOp->getSubExpr()))
+ return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
+
+ if (needsZeroComparison(UnOp->getSubExpr()))
+ return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
+
+ return replacementExpression(Result, false, UnOp->getSubExpr());
+ }
+ }
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Result, E, false);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Result, E, false);
+
+ StringRef NegatedOperator;
+ const Expr *LHS = nullptr;
+ const Expr *RHS = nullptr;
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+ NegatedOperator = negatedOperator(BinOp);
+ LHS = BinOp->getLHS();
+ RHS = BinOp->getRHS();
+ } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+ if (OpExpr->getNumArgs() == 2) {
+ NegatedOperator = negatedOperator(OpExpr);
+ LHS = OpExpr->getArg(0);
+ RHS = OpExpr->getArg(1);
+ }
+ }
+ if (!NegatedOperator.empty() && LHS && RHS)
+ return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
+ getText(Result, *RHS))
+ .str(),
+ NeedsStaticCast));
+
+ StringRef Text = getText(Result, *E);
+ if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
+ return ("!(" + Text + ")").str();
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Result, E, false);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Result, E, false);
+
+ return ("!" + asBool(Text, NeedsStaticCast));
+ }
+
+ if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+ if (UnOp->getOpcode() == UO_LNot) {
+ if (needsNullPtrComparison(UnOp->getSubExpr()))
+ return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
+
+ if (needsZeroComparison(UnOp->getSubExpr()))
+ return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
+ }
+ }
+
+ if (needsNullPtrComparison(E))
+ return compareExpressionToNullPtr(Result, E, true);
+
+ if (needsZeroComparison(E))
+ return compareExpressionToZero(Result, E, true);
+
+ return asBool(getText(Result, *E), NeedsStaticCast);
+}
+
+const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
+ if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
+ if (Bool->getValue() == !Negated)
+ return Bool;
+ }
+
+ return nullptr;
+}
+
+const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
+ if (IfRet->getElse() != nullptr)
+ return nullptr;
+
+ if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
+ return stmtReturnsBool(Ret, Negated);
+
+ if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
+ if (Compound->size() == 1) {
+ if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
+ return stmtReturnsBool(CompoundRet, Negated);
+ }
+ }
+
+ return nullptr;
+}
+
+bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
+ CharSourceRange CharRange) {
+ std::string ReplacementText =
+ Lexer::getSourceText(CharRange, *Result.SourceManager,
+ Result.Context->getLangOpts())
+ .str();
+ Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
+ ReplacementText.data(), ReplacementText.data(),
+ ReplacementText.data() + ReplacementText.size());
+ Lex.SetCommentRetentionState(true);
+
+ Token Tok;
+ while (!Lex.LexFromRawLexer(Tok)) {
+ if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
+ using Base = RecursiveASTVisitor<Visitor>;
+
+ public:
+ Visitor(SimplifyBooleanExprCheck *Check,
+ const MatchFinder::MatchResult &Result)
+ : Check(Check), Result(Result) {}
+
+ bool VisitBinaryOperator(BinaryOperator *Op) {
+ Check->reportBinOp(Result, Op);
+ return true;
+ }
+
+ private:
+ SimplifyBooleanExprCheck *Check;
+ const MatchFinder::MatchResult &Result;
+};
+
+
+SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
+ ChainedConditionalAssignment(
+ Options.get("ChainedConditionalAssignment", 0U)) {}
+
+bool containsBoolLiteral(const Expr *E) {
+ if (!E)
+ return false;
+ E = E->IgnoreParenImpCasts();
+ if (isa<CXXBoolLiteralExpr>(E))
+ return true;
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
+ return containsBoolLiteral(BinOp->getLHS()) ||
+ containsBoolLiteral(BinOp->getRHS());
+ if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
+ return containsBoolLiteral(UnaryOp->getSubExpr());
+ return false;
+}
+
+void SimplifyBooleanExprCheck::reportBinOp(
+ const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
+ const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
+ const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
+
+ const CXXBoolLiteralExpr *Bool = nullptr;
+ const Expr *Other = nullptr;
+ if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
+ Other = RHS;
+ else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
+ Other = LHS;
+ else
+ return;
+
+ if (Bool->getLocStart().isMacroID())
+ return;
+
+ // FIXME: why do we need this?
+ if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
+ return;
+
+ bool BoolValue = Bool->getValue();
+
+ auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
+ const Expr *ReplaceWith, bool Negated) {
+ std::string Replacement =
+ replacementExpression(Result, Negated, ReplaceWith);
+ SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
+ issueDiag(Result, Bool->getLocStart(), SimplifyOperatorDiagnostic, Range,
+ Replacement);
+ };
+
+ switch (Op->getOpcode()) {
+ case BO_LAnd:
+ if (BoolValue) {
+ // expr && true -> expr
+ replaceWithExpression(Other, /*Negated=*/false);
+ } else {
+ // expr && false -> false
+ replaceWithExpression(Bool, /*Negated=*/false);
+ }
+ break;
+ case BO_LOr:
+ if (BoolValue) {
+ // expr || true -> true
+ replaceWithExpression(Bool, /*Negated=*/false);
+ } else {
+ // expr || false -> expr
+ replaceWithExpression(Other, /*Negated=*/false);
+ }
+ break;
+ case BO_EQ:
+ // expr == true -> expr, expr == false -> !expr
+ replaceWithExpression(Other, /*Negated=*/!BoolValue);
+ break;
+ case BO_NE:
+ // expr != true -> !expr, expr != false -> expr
+ replaceWithExpression(Other, /*Negated=*/BoolValue);
+ break;
+ default:
+ break;
+ }
+}
+
+void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
+ bool Value,
+ StringRef BooleanId) {
+ Finder->addMatcher(
+ ifStmt(isExpansionInMainFile(),
+ hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
+ .bind(IfStmtId),
+ this);
+}
+
+void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
+ bool Value,
+ StringRef TernaryId) {
+ Finder->addMatcher(
+ conditionalOperator(isExpansionInMainFile(),
+ hasTrueExpression(cxxBoolLiteral(equals(Value))),
+ hasFalseExpression(cxxBoolLiteral(equals(!Value))))
+ .bind(TernaryId),
+ this);
+}
+
+void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
+ bool Value, StringRef Id) {
+ if (ChainedConditionalReturn)
+ Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+ hasThen(returnsBool(Value, ThenLiteralId)),
+ hasElse(returnsBool(!Value)))
+ .bind(Id),
+ this);
+ else
+ Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+ unless(hasParent(ifStmt())),
+ hasThen(returnsBool(Value, ThenLiteralId)),
+ hasElse(returnsBool(!Value)))
+ .bind(Id),
+ this);
+}
+
+void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
+ bool Value, StringRef Id) {
+ auto SimpleThen = binaryOperator(
+ hasOperatorName("="),
+ hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
+ hasLHS(expr().bind(IfAssignVariableId)),
+ hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
+ auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
+ hasAnySubstatement(SimpleThen)));
+ auto SimpleElse = binaryOperator(
+ hasOperatorName("="),
+ hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
+ hasRHS(cxxBoolLiteral(equals(!Value))));
+ auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
+ hasAnySubstatement(SimpleElse)));
+ if (ChainedConditionalAssignment)
+ Finder->addMatcher(
+ ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
+ this);
+ else
+ Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+ unless(hasParent(ifStmt())), hasThen(Then),
+ hasElse(Else))
+ .bind(Id),
+ this);
+}
+
+void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
+ bool Value,
+ StringRef Id) {
+ Finder->addMatcher(
+ compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
+ unless(hasElse(stmt())))),
+ hasAnySubstatement(
+ returnStmt(has(ignoringParenImpCasts(
+ cxxBoolLiteral(equals(!Value)))))
+ .bind(CompoundReturnId))))
+ .bind(Id),
+ this);
+}
+
+void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
+ Options.store(Opts, "ChainedConditionalAssignment",
+ ChainedConditionalAssignment);
+}
+
+void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(translationUnitDecl().bind("top"), this);
+
+ matchBoolCondition(Finder, true, ConditionThenStmtId);
+ matchBoolCondition(Finder, false, ConditionElseStmtId);
+
+ matchTernaryResult(Finder, true, TernaryId);
+ matchTernaryResult(Finder, false, TernaryNegatedId);
+
+ matchIfReturnsBool(Finder, true, IfReturnsBoolId);
+ matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
+
+ matchIfAssignsBool(Finder, true, IfAssignBoolId);
+ matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
+
+ matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
+ matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
+}
+
+void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const CXXBoolLiteralExpr *TrueConditionRemoved =
+ getBoolLiteral(Result, ConditionThenStmtId))
+ replaceWithThenStatement(Result, TrueConditionRemoved);
+ else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
+ getBoolLiteral(Result, ConditionElseStmtId))
+ replaceWithElseStatement(Result, FalseConditionRemoved);
+ else if (const auto *Ternary =
+ Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
+ replaceWithCondition(Result, Ternary);
+ else if (const auto *TernaryNegated =
+ Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
+ replaceWithCondition(Result, TernaryNegated, true);
+ else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
+ replaceWithReturnCondition(Result, If);
+ else if (const auto *IfNot =
+ Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
+ replaceWithReturnCondition(Result, IfNot, true);
+ else if (const auto *IfAssign =
+ Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
+ replaceWithAssignment(Result, IfAssign);
+ else if (const auto *IfAssignNot =
+ Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
+ replaceWithAssignment(Result, IfAssignNot, true);
+ else if (const auto *Compound =
+ Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
+ replaceCompoundReturnWithCondition(Result, Compound);
+ else if (const auto *Compound =
+ Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
+ replaceCompoundReturnWithCondition(Result, Compound, true);
+ else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top"))
+ Visitor(this, Result).TraverseDecl(const_cast<Decl*>(TU));
+}
+
+void SimplifyBooleanExprCheck::issueDiag(
+ const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
+ StringRef Description, SourceRange ReplacementRange,
+ StringRef Replacement) {
+ CharSourceRange CharRange =
+ Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
+ *Result.SourceManager, getLangOpts());
+
+ DiagnosticBuilder Diag = diag(Loc, Description);
+ if (!containsDiscardedTokens(Result, CharRange))
+ Diag << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithThenStatement(
+ const MatchFinder::MatchResult &Result,
+ const CXXBoolLiteralExpr *TrueConditionRemoved) {
+ const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
+ issueDiag(Result, TrueConditionRemoved->getLocStart(),
+ SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
+ getText(Result, *IfStatement->getThen()));
+}
+
+void SimplifyBooleanExprCheck::replaceWithElseStatement(
+ const MatchFinder::MatchResult &Result,
+ const CXXBoolLiteralExpr *FalseConditionRemoved) {
+ const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
+ const Stmt *ElseStatement = IfStatement->getElse();
+ issueDiag(Result, FalseConditionRemoved->getLocStart(),
+ SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
+ ElseStatement ? getText(Result, *ElseStatement) : "");
+}
+
+void SimplifyBooleanExprCheck::replaceWithCondition(
+ const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
+ bool Negated) {
+ std::string Replacement =
+ replacementExpression(Result, Negated, Ternary->getCond());
+ issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
+ "redundant boolean literal in ternary expression result",
+ Ternary->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithReturnCondition(
+ const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
+ StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
+ std::string Condition = replacementExpression(Result, Negated, If->getCond());
+ std::string Replacement = ("return " + Condition + Terminator).str();
+ SourceLocation Start =
+ Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
+ issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
+ If->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
+ const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
+ bool Negated) {
+ const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
+
+ // The body shouldn't be empty because the matcher ensures that it must
+ // contain at least two statements:
+ // 1) A `return` statement returning a boolean literal `false` or `true`
+ // 2) An `if` statement with no `else` clause that consists of a single
+ // `return` statement returning the opposite boolean literal `true` or
+ // `false`.
+ assert(Compound->size() >= 2);
+ const IfStmt *BeforeIf = nullptr;
+ CompoundStmt::const_body_iterator Current = Compound->body_begin();
+ CompoundStmt::const_body_iterator After = Compound->body_begin();
+ for (++After; After != Compound->body_end() && *Current != Ret;
+ ++Current, ++After) {
+ if (const auto *If = dyn_cast<IfStmt>(*Current)) {
+ if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
+ if (*After == Ret) {
+ if (!ChainedConditionalReturn && BeforeIf)
+ continue;
+
+ const Expr *Condition = If->getCond();
+ std::string Replacement =
+ "return " + replacementExpression(Result, Negated, Condition);
+ issueDiag(
+ Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
+ SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
+ return;
+ }
+
+ BeforeIf = If;
+ }
+ } else {
+ BeforeIf = nullptr;
+ }
+ }
+}
+
+void SimplifyBooleanExprCheck::replaceWithAssignment(
+ const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
+ bool Negated) {
+ SourceRange Range = IfAssign->getSourceRange();
+ StringRef VariableName =
+ getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
+ StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
+ std::string Condition =
+ replacementExpression(Result, Negated, IfAssign->getCond());
+ std::string Replacement =
+ (VariableName + " = " + Condition + Terminator).str();
+ SourceLocation Location =
+ Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
+ issueDiag(Result, Location,
+ "redundant boolean literal in conditional assignment", Range,
+ Replacement);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- SimplifyBooleanExpr.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_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Looks for boolean expressions involving boolean constants and simplifies
+/// them to use the appropriate boolean expression directly.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-simplify-boolean-expr.html
+class SimplifyBooleanExprCheck : public ClangTidyCheck {
+public:
+ SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context);
+
+ void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ class Visitor;
+
+ void reportBinOp(const ast_matchers::MatchFinder::MatchResult &Result,
+ const BinaryOperator *Op);
+
+ void matchBoolCondition(ast_matchers::MatchFinder *Finder, bool Value,
+ StringRef BooleanId);
+
+ void matchTernaryResult(ast_matchers::MatchFinder *Finder, bool Value,
+ StringRef TernaryId);
+
+ void matchIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value,
+ StringRef Id);
+
+ void matchIfAssignsBool(ast_matchers::MatchFinder *Finder, bool Value,
+ StringRef Id);
+
+ void matchCompoundIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value,
+ StringRef Id);
+
+ void
+ replaceWithThenStatement(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXBoolLiteralExpr *BoolLiteral);
+
+ void
+ replaceWithElseStatement(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXBoolLiteralExpr *FalseConditionRemoved);
+
+ void
+ replaceWithCondition(const ast_matchers::MatchFinder::MatchResult &Result,
+ const ConditionalOperator *Ternary,
+ bool Negated = false);
+
+ void replaceWithReturnCondition(
+ const ast_matchers::MatchFinder::MatchResult &Result, const IfStmt *If,
+ bool Negated = false);
+
+ void
+ replaceWithAssignment(const ast_matchers::MatchFinder::MatchResult &Result,
+ const IfStmt *If, bool Negated = false);
+
+ void replaceCompoundReturnWithCondition(
+ const ast_matchers::MatchFinder::MatchResult &Result,
+ const CompoundStmt *Compound, bool Negated = false);
+
+ void issueDiag(const ast_matchers::MatchFinder::MatchResult &Result,
+ SourceLocation Loc, StringRef Description,
+ SourceRange ReplacementRange, StringRef Replacement);
+
+ const bool ChainedConditionalReturn;
+ const bool ChainedConditionalAssignment;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
--- /dev/null
+//===--- StaticDefinitionInAnonymousNamespaceCheck.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 "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void StaticDefinitionInAnonymousNamespaceCheck::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(
+ namedDecl(anyOf(functionDecl(isDefinition(), isStaticStorageClass()),
+ varDecl(isDefinition(), isStaticStorageClass())),
+ hasParent(namespaceDecl(isAnonymous())))
+ .bind("static-def"),
+ this);
+}
+
+void StaticDefinitionInAnonymousNamespaceCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Def = Result.Nodes.getNodeAs<NamedDecl>("static-def");
+ // Skips all static definitions defined in Macro.
+ if (Def->getLocation().isMacroID())
+ return;
+
+ // Skips all static definitions in function scope.
+ const DeclContext *DC = Def->getDeclContext();
+ if (DC->getDeclKind() != Decl::Namespace)
+ return;
+
+ auto Diag =
+ diag(Def->getLocation(), "%0 is a static definition in "
+ "anonymous namespace; static is redundant here")
+ << Def;
+ Token Tok;
+ SourceLocation Loc = Def->getSourceRange().getBegin();
+ while (Loc < Def->getSourceRange().getEnd() &&
+ !Lexer::getRawToken(Loc, Tok, *Result.SourceManager, getLangOpts(),
+ true)) {
+ SourceRange TokenRange(Tok.getLocation(), Tok.getEndLoc());
+ StringRef SourceText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(TokenRange),
+ *Result.SourceManager, getLangOpts());
+ if (SourceText == "static") {
+ Diag << FixItHint::CreateRemoval(TokenRange);
+ break;
+ }
+ Loc = Tok.getEndLoc();
+ }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- StaticDefinitionInAnonymousNamespaceCheck.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_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds static function and variable definitions in anonymous namespace.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.html
+class StaticDefinitionInAnonymousNamespaceCheck : public ClangTidyCheck {
+public:
+ StaticDefinitionInAnonymousNamespaceCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
--- /dev/null
+//===--- UniqueptrDeleteReleaseCheck.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 "UniqueptrDeleteReleaseCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) {
+ auto IsSusbstituted = qualType(anyOf(
+ substTemplateTypeParmType(), hasDescendant(substTemplateTypeParmType())));
+
+ auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl(
+ hasName("std::unique_ptr"),
+ hasTemplateArgument(1, refersToType(qualType(hasDeclaration(cxxRecordDecl(
+ hasName("std::default_delete")))))));
+
+ Finder->addMatcher(
+ cxxDeleteExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+ on(expr(hasType(UniquePtrWithDefaultDelete),
+ unless(hasType(IsSusbstituted)))
+ .bind("uptr")),
+ callee(cxxMethodDecl(hasName("release")))))))
+ .bind("delete"),
+ this);
+}
+
+void UniqueptrDeleteReleaseCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *PtrExpr = Result.Nodes.getNodeAs<Expr>("uptr");
+ const auto *DeleteExpr = Result.Nodes.getNodeAs<Expr>("delete");
+
+ if (PtrExpr->getLocStart().isMacroID())
+ return;
+
+ // Ignore dependent types.
+ // It can give us false positives, so we go with false negatives instead to
+ // be safe.
+ if (PtrExpr->getType()->isDependentType())
+ return;
+
+ SourceLocation AfterPtr = Lexer::getLocForEndOfToken(
+ PtrExpr->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+
+ diag(DeleteExpr->getLocStart(),
+ "prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> "
+ "objects")
+ << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+ DeleteExpr->getLocStart(), PtrExpr->getLocStart()))
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(AfterPtr, DeleteExpr->getLocEnd()),
+ " = nullptr");
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- UniqueptrDeleteReleaseCheck.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_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Flags statements of the form ``delete <unique_ptr expr>.release();`` and
+/// replaces them with: ``<unique_ptr expr> = nullptr;``
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-uniqueptr-delete-release.html
+class UniqueptrDeleteReleaseCheck : public ClangTidyCheck {
+public:
+ UniqueptrDeleteReleaseCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
--- /dev/null
+#!/usr/bin/env python
+#
+#===- rename_check.py - clang-tidy check renamer -------------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+import os
+import glob
+import argparse
+
+
+def replaceInFile(fileName, sFrom, sTo):
+ if sFrom == sTo:
+ return
+ txt = None
+ with open(fileName, "r") as f:
+ txt = f.read()
+
+ if sFrom not in txt:
+ return
+
+ txt = txt.replace(sFrom, sTo)
+ print("Replace '%s' -> '%s' in '%s'" % (sFrom, sTo, fileName))
+ with open(fileName, "w") as f:
+ f.write(txt)
+
+
+def generateCommentLineHeader(filename):
+ return ''.join(['//===--- ',
+ os.path.basename(filename),
+ ' - clang-tidy ',
+ '-' * max(0, 42 - len(os.path.basename(filename))),
+ '*- C++ -*-===//'])
+
+
+def generateCommentLineSource(filename):
+ return ''.join(['//===--- ',
+ os.path.basename(filename),
+ ' - clang-tidy',
+ '-' * max(0, 52 - len(os.path.basename(filename))),
+ '-===//'])
+
+
+def fileRename(fileName, sFrom, sTo):
+ if sFrom not in fileName:
+ return fileName
+ newFileName = fileName.replace(sFrom, sTo)
+ print("Rename '%s' -> '%s'" % (fileName, newFileName))
+ os.rename(fileName, newFileName)
+ return newFileName
+
+
+def getListOfFiles(clang_tidy_path):
+ files = glob.glob(os.path.join(clang_tidy_path, '*'))
+ for dirname in files:
+ if os.path.isdir(dirname):
+ files += glob.glob(os.path.join(dirname, '*'))
+ files += glob.glob(os.path.join(clang_tidy_path, '..', 'test',
+ 'clang-tidy', '*'))
+ files += glob.glob(os.path.join(clang_tidy_path, '..', 'docs',
+ 'clang-tidy', 'checks', '*'))
+ return [filename for filename in files if os.path.isfile(filename)]
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Rename clang-tidy check.')
+ parser.add_argument('module', type=str,
+ help='Module where the renamed check is defined')
+ parser.add_argument('old_check_name', type=str,
+ help='Old check name.')
+ parser.add_argument('new_check_name', type=str,
+ help='New check name.')
+ args = parser.parse_args()
+
+ args.module = args.module.lower()
+ check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
+ args.old_check_name.split('-'))) + 'Check'
+ check_name_new_camel = (''.join(map(lambda elem: elem.capitalize(),
+ args.new_check_name.split('-'))) +
+ 'Check')
+
+ clang_tidy_path = os.path.dirname(__file__)
+
+ header_guard_old = (args.module.upper() + '_' +
+ args.old_check_name.upper().replace('-', '_'))
+ header_guard_new = (args.module.upper() + '_' +
+ args.new_check_name.upper().replace('-', '_'))
+
+ for filename in getListOfFiles(clang_tidy_path):
+ originalName = filename
+ filename = fileRename(filename, args.old_check_name,
+ args.new_check_name)
+ filename = fileRename(filename, check_name_camel, check_name_new_camel)
+ replaceInFile(filename, generateCommentLineHeader(originalName),
+ generateCommentLineHeader(filename))
+ replaceInFile(filename, generateCommentLineSource(originalName),
+ generateCommentLineSource(filename))
+ replaceInFile(filename, header_guard_old, header_guard_new)
+ replaceInFile(filename, args.old_check_name, args.new_check_name)
+ replaceInFile(filename, check_name_camel, check_name_new_camel)
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_tool(clang-tidy
+ ClangTidyMain.cpp
+ )
+add_dependencies(clang-tidy
+ clang-headers
+ )
+target_link_libraries(clang-tidy
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangTidy
+ clangTidyAndroidModule
+ clangTidyBoostModule
+ clangTidyBugproneModule
+ clangTidyCERTModule
+ clangTidyCppCoreGuidelinesModule
+ clangTidyGoogleModule
+ clangTidyHICPPModule
+ clangTidyLLVMModule
+ clangTidyMiscModule
+ clangTidyModernizeModule
+ clangTidyMPIModule
+ clangTidyPerformanceModule
+ clangTidyReadabilityModule
+ clangTooling
+ clangToolingCore
+ )
+
+install(TARGETS clang-tidy
+ RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang)
+install(PROGRAMS run-clang-tidy.py DESTINATION share/clang)
--- /dev/null
+//===--- tools/extra/clang-tidy/ClangTidyMain.cpp - Clang tidy tool -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file implements a clang-tidy tool.
+///
+/// This tool uses the Clang Tooling infrastructure, see
+/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+/// for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "llvm/Support/Process.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::driver;
+using namespace clang::tooling;
+using namespace llvm;
+
+static cl::OptionCategory ClangTidyCategory("clang-tidy options");
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::extrahelp ClangTidyHelp(R"(
+Configuration files:
+ clang-tidy attempts to read configuration for each source file from a
+ .clang-tidy file located in the closest parent directory of the source
+ file. If any configuration options have a corresponding command-line
+ option, command-line option takes precedence. The effective
+ configuration can be inspected using -dump-config:
+
+ $ clang-tidy -dump-config
+ ---
+ Checks: '-*,some-check'
+ WarningsAsErrors: ''
+ HeaderFilterRegex: ''
+ AnalyzeTemporaryDtors: false
+ FormatStyle: none
+ User: user
+ CheckOptions:
+ - key: some-check.SomeOption
+ value: 'some value'
+ ...
+
+)");
+
+const char DefaultChecks[] = // Enable these checks by default:
+ "clang-diagnostic-*," // * compiler diagnostics
+ "clang-analyzer-*"; // * Static Analyzer checks
+
+static cl::opt<std::string> Checks("checks", cl::desc(R"(
+Comma-separated list of globs with optional '-'
+prefix. Globs are processed in order of
+appearance in the list. Globs without '-'
+prefix add checks with matching names to the
+set, globs with the '-' prefix remove checks
+with matching names from the set of enabled
+checks. This option's value is appended to the
+value of the 'Checks' option in .clang-tidy
+file, if any.
+)"),
+ cl::init(""), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", cl::desc(R"(
+Upgrades warnings to errors. Same format as
+'-checks'.
+This option's value is appended to the value of
+the 'WarningsAsErrors' option in .clang-tidy
+file, if any.
+)"),
+ cl::init(""),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> HeaderFilter("header-filter", cl::desc(R"(
+Regular expression matching the names of the
+headers to output diagnostics from. Diagnostics
+from the main file of each translation unit are
+always displayed.
+Can be used together with -line-filter.
+This option overrides the 'HeaderFilter' option
+in .clang-tidy file, if any.
+)"),
+ cl::init(""),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<bool>
+ SystemHeaders("system-headers",
+ cl::desc("Display the errors from system headers."),
+ cl::init(false), cl::cat(ClangTidyCategory));
+static cl::opt<std::string> LineFilter("line-filter", cl::desc(R"(
+List of files with line ranges to filter the
+warnings. Can be used together with
+-header-filter. The format of the list is a
+JSON array of objects:
+ [
+ {"name":"file1.cpp","lines":[[1,3],[5,7]]},
+ {"name":"file2.h"}
+ ]
+)"),
+ cl::init(""),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> Fix("fix", cl::desc(R"(
+Apply suggested fixes. Without -fix-errors
+clang-tidy will bail out if any compilation
+errors were found.
+)"),
+ cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> FixErrors("fix-errors", cl::desc(R"(
+Apply suggested fixes even if compilation
+errors were found. If compiler errors have
+attached fix-its, clang-tidy will apply them as
+well.
+)"),
+ cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> FormatStyle("format-style", cl::desc(R"(
+Style for formatting code around applied fixes:
+ - 'none' (default) turns off formatting
+ - 'file' (literally 'file', not a placeholder)
+ uses .clang-format file in the closest parent
+ directory
+ - '{ <json> }' specifies options inline, e.g.
+ -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
+ - 'llvm', 'google', 'webkit', 'mozilla'
+See clang-format documentation for the up-to-date
+information about formatting styles and options.
+This option overrides the 'FormatStyle` option in
+.clang-tidy file, if any.
+)"),
+ cl::init("none"),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> ListChecks("list-checks", cl::desc(R"(
+List all enabled checks and exit. Use with
+-checks=* to list all available checks.
+)"),
+ cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> ExplainConfig("explain-config", cl::desc(R"(
+For each enabled check explains, where it is
+enabled, i.e. in clang-tidy binary, command
+line or a specific configuration file.
+)"),
+ cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> Config("config", cl::desc(R"(
+Specifies a configuration in YAML/JSON format:
+ -config="{Checks: '*',
+ CheckOptions: [{key: x,
+ value: y}]}"
+When the value is empty, clang-tidy will
+attempt to find a file named .clang-tidy for
+each source file in its parent directories.
+)"),
+ cl::init(""), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> DumpConfig("dump-config", cl::desc(R"(
+Dumps configuration in the YAML format to
+stdout. This option can be used along with a
+file name (and '--' if the file is outside of a
+project with configured compilation database).
+The configuration used for this file will be
+printed.
+Use along with -checks=* to include
+configuration of all checks.
+)"),
+ cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> EnableCheckProfile("enable-check-profile", cl::desc(R"(
+Enable per-check timing profiles, and print a
+report to stderr.
+)"),
+ cl::init(false),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> AnalyzeTemporaryDtors("analyze-temporary-dtors",
+ cl::desc(R"(
+Enable temporary destructor-aware analysis in
+clang-analyzer- checks.
+This option overrides the value read from a
+.clang-tidy file.
+)"),
+ cl::init(false),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> ExportFixes("export-fixes", cl::desc(R"(
+YAML file to store suggested fixes in. The
+stored fixes can be applied to the input source
+code with clang-apply-replacements.
+)"),
+ cl::value_desc("filename"),
+ cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> Quiet("quiet", cl::desc(R"(
+Run clang-tidy in quiet mode. This suppresses
+printing statistics about ignored warnings and
+warnings treated as errors if the respective
+options are specified.
+)"),
+ cl::init(false),
+ cl::cat(ClangTidyCategory));
+
+namespace clang {
+namespace tidy {
+
+static void printStats(const ClangTidyStats &Stats) {
+ if (Stats.errorsIgnored()) {
+ llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings (";
+ StringRef Separator = "";
+ if (Stats.ErrorsIgnoredNonUserCode) {
+ llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";
+ Separator = ", ";
+ }
+ if (Stats.ErrorsIgnoredLineFilter) {
+ llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter
+ << " due to line filter";
+ Separator = ", ";
+ }
+ if (Stats.ErrorsIgnoredNOLINT) {
+ llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
+ Separator = ", ";
+ }
+ if (Stats.ErrorsIgnoredCheckFilter)
+ llvm::errs() << Separator << Stats.ErrorsIgnoredCheckFilter
+ << " with check filters";
+ llvm::errs() << ").\n";
+ if (Stats.ErrorsIgnoredNonUserCode)
+ llvm::errs() << "Use -header-filter=.* to display errors from all "
+ "non-system headers. Use -system-headers to display "
+ "errors from system headers as well.\n";
+ }
+}
+
+static void printProfileData(const ProfileData &Profile,
+ llvm::raw_ostream &OS) {
+ // Time is first to allow for sorting by it.
+ std::vector<std::pair<llvm::TimeRecord, StringRef>> Timers;
+ TimeRecord Total;
+
+ for (const auto &P : Profile.Records) {
+ Timers.emplace_back(P.getValue(), P.getKey());
+ Total += P.getValue();
+ }
+
+ std::sort(Timers.begin(), Timers.end());
+
+ std::string Line = "===" + std::string(73, '-') + "===\n";
+ OS << Line;
+
+ if (Total.getUserTime())
+ OS << " ---User Time---";
+ if (Total.getSystemTime())
+ OS << " --System Time--";
+ if (Total.getProcessTime())
+ OS << " --User+System--";
+ OS << " ---Wall Time---";
+ if (Total.getMemUsed())
+ OS << " ---Mem---";
+ OS << " --- Name ---\n";
+
+ // Loop through all of the timing data, printing it out.
+ for (auto I = Timers.rbegin(), E = Timers.rend(); I != E; ++I) {
+ I->first.print(Total, OS);
+ OS << I->second << '\n';
+ }
+
+ Total.print(Total, OS);
+ OS << "Total\n";
+ OS << Line << "\n";
+ OS.flush();
+}
+
+static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider() {
+ ClangTidyGlobalOptions GlobalOptions;
+ if (std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) {
+ llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";
+ llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+ return nullptr;
+ }
+
+ ClangTidyOptions DefaultOptions;
+ DefaultOptions.Checks = DefaultChecks;
+ DefaultOptions.WarningsAsErrors = "";
+ DefaultOptions.HeaderFilterRegex = HeaderFilter;
+ DefaultOptions.SystemHeaders = SystemHeaders;
+ DefaultOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+ DefaultOptions.FormatStyle = FormatStyle;
+ DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
+ // USERNAME is used on Windows.
+ if (!DefaultOptions.User)
+ DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME");
+
+ ClangTidyOptions OverrideOptions;
+ if (Checks.getNumOccurrences() > 0)
+ OverrideOptions.Checks = Checks;
+ if (WarningsAsErrors.getNumOccurrences() > 0)
+ OverrideOptions.WarningsAsErrors = WarningsAsErrors;
+ if (HeaderFilter.getNumOccurrences() > 0)
+ OverrideOptions.HeaderFilterRegex = HeaderFilter;
+ if (SystemHeaders.getNumOccurrences() > 0)
+ OverrideOptions.SystemHeaders = SystemHeaders;
+ if (AnalyzeTemporaryDtors.getNumOccurrences() > 0)
+ OverrideOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+ if (FormatStyle.getNumOccurrences() > 0)
+ OverrideOptions.FormatStyle = FormatStyle;
+
+ if (!Config.empty()) {
+ if (llvm::ErrorOr<ClangTidyOptions> ParsedConfig =
+ parseConfiguration(Config)) {
+ return llvm::make_unique<ConfigOptionsProvider>(
+ GlobalOptions,
+ ClangTidyOptions::getDefaults().mergeWith(DefaultOptions),
+ *ParsedConfig, OverrideOptions);
+ } else {
+ llvm::errs() << "Error: invalid configuration specified.\n"
+ << ParsedConfig.getError().message() << "\n";
+ return nullptr;
+ }
+ }
+ return llvm::make_unique<FileOptionsProvider>(GlobalOptions, DefaultOptions,
+ OverrideOptions);
+}
+
+static int clangTidyMain(int argc, const char **argv) {
+ CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory,
+ cl::ZeroOrMore);
+
+ auto OwningOptionsProvider = createOptionsProvider();
+ auto *OptionsProvider = OwningOptionsProvider.get();
+ if (!OptionsProvider)
+ return 1;
+
+ StringRef FileName("dummy");
+ auto PathList = OptionsParser.getSourcePathList();
+ if (!PathList.empty()) {
+ FileName = PathList.front();
+ }
+
+ SmallString<256> FilePath(FileName);
+ if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
+ llvm::errs() << "Can't make absolute path from " << FileName << ": "
+ << EC.message() << "\n";
+ }
+ ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
+ std::vector<std::string> EnabledChecks = getCheckNames(EffectiveOptions);
+
+ if (ExplainConfig) {
+ // FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
+ std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
+ RawOptions = OptionsProvider->getRawOptions(FilePath);
+ for (const std::string &Check : EnabledChecks) {
+ for (auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) {
+ if (It->first.Checks && GlobList(*It->first.Checks).contains(Check)) {
+ llvm::outs() << "'" << Check << "' is enabled in the " << It->second
+ << ".\n";
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+
+ if (ListChecks) {
+ if (EnabledChecks.empty()) {
+ llvm::errs() << "No checks enabled.\n";
+ return 1;
+ }
+ llvm::outs() << "Enabled checks:";
+ for (const auto &CheckName : EnabledChecks)
+ llvm::outs() << "\n " << CheckName;
+ llvm::outs() << "\n\n";
+ return 0;
+ }
+
+ if (DumpConfig) {
+ EffectiveOptions.CheckOptions = getCheckOptions(EffectiveOptions);
+ llvm::outs() << configurationAsText(
+ ClangTidyOptions::getDefaults().mergeWith(
+ EffectiveOptions))
+ << "\n";
+ return 0;
+ }
+
+ if (EnabledChecks.empty()) {
+ llvm::errs() << "Error: no checks enabled.\n";
+ llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+ return 1;
+ }
+
+ if (PathList.empty()) {
+ llvm::errs() << "Error: no input files specified.\n";
+ llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+ return 1;
+ }
+
+ ProfileData Profile;
+
+ ClangTidyContext Context(std::move(OwningOptionsProvider));
+ runClangTidy(Context, OptionsParser.getCompilations(), PathList,
+ EnableCheckProfile ? &Profile : nullptr);
+ ArrayRef<ClangTidyError> Errors = Context.getErrors();
+ bool FoundErrors =
+ std::find_if(Errors.begin(), Errors.end(), [](const ClangTidyError &E) {
+ return E.DiagLevel == ClangTidyError::Error;
+ }) != Errors.end();
+
+ const bool DisableFixes = Fix && FoundErrors && !FixErrors;
+
+ unsigned WErrorCount = 0;
+
+ // -fix-errors implies -fix.
+ handleErrors(Context, (FixErrors || Fix) && !DisableFixes, WErrorCount);
+
+ if (!ExportFixes.empty() && !Errors.empty()) {
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+ return 1;
+ }
+ exportReplacements(FilePath.str(), Errors, OS);
+ }
+
+ if (!Quiet) {
+ printStats(Context.getStats());
+ if (DisableFixes)
+ llvm::errs()
+ << "Found compiler errors, but -fix-errors was not specified.\n"
+ "Fixes have NOT been applied.\n\n";
+ }
+
+ if (EnableCheckProfile)
+ printProfileData(Profile, llvm::errs());
+
+ if (WErrorCount) {
+ if (!Quiet) {
+ StringRef Plural = WErrorCount == 1 ? "" : "s";
+ llvm::errs() << WErrorCount << " warning" << Plural << " treated as error"
+ << Plural << "\n";
+ }
+ return WErrorCount;
+ }
+
+ return 0;
+}
+
+// This anchor is used to force the linker to link the CERTModule.
+extern volatile int CERTModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CERTModuleAnchorDestination =
+ CERTModuleAnchorSource;
+
+// This anchor is used to force the linker to link the BoostModule.
+extern volatile int BoostModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED BoostModuleAnchorDestination =
+ BoostModuleAnchorSource;
+
+// This anchor is used to force the linker to link the BugproneModule.
+extern volatile int BugproneModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED BugproneModuleAnchorDestination =
+ BugproneModuleAnchorSource;
+
+// This anchor is used to force the linker to link the LLVMModule.
+extern volatile int LLVMModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED LLVMModuleAnchorDestination =
+ LLVMModuleAnchorSource;
+
+// This anchor is used to force the linker to link the CppCoreGuidelinesModule.
+extern volatile int CppCoreGuidelinesModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
+ CppCoreGuidelinesModuleAnchorSource;
+
+// This anchor is used to force the linker to link the GoogleModule.
+extern volatile int GoogleModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination =
+ GoogleModuleAnchorSource;
+
+// This anchor is used to force the linker to link the AndroidModule.
+extern volatile int AndroidModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED AndroidModuleAnchorDestination =
+ AndroidModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MiscModule.
+extern volatile int MiscModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MiscModuleAnchorDestination =
+ MiscModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ModernizeModule.
+extern volatile int ModernizeModuleAnchorSource;
+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 =
+ PerformanceModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ReadabilityModule.
+extern volatile int ReadabilityModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ReadabilityModuleAnchorDestination =
+ ReadabilityModuleAnchorSource;
+
+// This anchor is used to force the linker to link the HICPPModule.
+extern volatile int HICPPModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED HICPPModuleAnchorDestination =
+ HICPPModuleAnchorSource;
+
+} // namespace tidy
+} // namespace clang
+
+int main(int argc, const char **argv) {
+ return clang::tidy::clangTidyMain(argc, argv);
+}
--- /dev/null
+#!/usr/bin/env python
+#
+#===- clang-tidy-diff.py - ClangTidy Diff Checker ------------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+ClangTidy Diff Checker
+======================
+
+This script reads input from a unified diff, runs clang-tidy on all changed
+files and outputs clang-tidy warnings in changed lines only. This is useful to
+detect clang-tidy regressions in the lines touched by a specific patch.
+Example usage for git/svn users:
+
+ git diff -U0 HEAD^ | clang-tidy-diff.py -p1
+ svn diff --diff-cmd=diff -x-U0 | \
+ clang-tidy-diff.py -fix -checks=-*,modernize-use-override
+
+"""
+
+import argparse
+import json
+import re
+import subprocess
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser(description=
+ 'Run clang-tidy against changed files, and '
+ 'output diagnostics only for modified '
+ 'lines.')
+ parser.add_argument('-clang-tidy-binary', metavar='PATH',
+ default='clang-tidy',
+ help='path to clang-tidy binary')
+ parser.add_argument('-p', metavar='NUM', default=0,
+ help='strip the smallest prefix containing P slashes')
+ parser.add_argument('-regex', metavar='PATTERN', default=None,
+ help='custom pattern selecting file paths to check '
+ '(case sensitive, overrides -iregex)')
+ parser.add_argument('-iregex', metavar='PATTERN', default=
+ r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)',
+ help='custom pattern selecting file paths to check '
+ '(case insensitive, overridden by -regex)')
+
+ parser.add_argument('-fix', action='store_true', default=False,
+ help='apply suggested fixes')
+ parser.add_argument('-checks',
+ help='checks filter, when not specified, use clang-tidy '
+ 'default',
+ default='')
+ parser.add_argument('-path', dest='build_path',
+ help='Path used to read a compile command database.')
+ parser.add_argument('-extra-arg', dest='extra_arg',
+ action='append', default=[],
+ help='Additional argument to append to the compiler '
+ 'command line.')
+ parser.add_argument('-extra-arg-before', dest='extra_arg_before',
+ action='append', default=[],
+ help='Additional argument to prepend to the compiler '
+ 'command line.')
+ parser.add_argument('-quiet', action='store_true', default=False,
+ help='Run clang-tidy in quiet mode')
+ clang_tidy_args = []
+ argv = sys.argv[1:]
+ if '--' in argv:
+ clang_tidy_args.extend(argv[argv.index('--'):])
+ argv = argv[:argv.index('--')]
+
+ args = parser.parse_args(argv)
+
+ # Extract changed lines for each file.
+ filename = None
+ lines_by_file = {}
+ for line in sys.stdin:
+ match = re.search('^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % args.p, line)
+ if match:
+ filename = match.group(2)
+ if filename == None:
+ continue
+
+ if args.regex is not None:
+ if not re.match('^%s$' % args.regex, filename):
+ continue
+ else:
+ if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
+ continue
+
+ match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
+ if match:
+ start_line = int(match.group(1))
+ line_count = 1
+ if match.group(3):
+ line_count = int(match.group(3))
+ if line_count == 0:
+ continue
+ end_line = start_line + line_count - 1;
+ lines_by_file.setdefault(filename, []).append([start_line, end_line])
+
+ if len(lines_by_file) == 0:
+ print("No relevant changes found.")
+ sys.exit(0)
+
+ line_filter_json = json.dumps(
+ [{"name" : name, "lines" : lines_by_file[name]} for name in lines_by_file],
+ separators = (',', ':'))
+
+ quote = "";
+ if sys.platform == 'win32':
+ line_filter_json=re.sub(r'"', r'"""', line_filter_json)
+ else:
+ quote = "'";
+
+ # Run clang-tidy on files containing changes.
+ command = [args.clang_tidy_binary]
+ command.append('-line-filter=' + quote + line_filter_json + quote)
+ if args.fix:
+ command.append('-fix')
+ if args.checks != '':
+ command.append('-checks=' + quote + args.checks + quote)
+ if args.quiet:
+ command.append('-quiet')
+ if args.build_path is not None:
+ command.append('-p=%s' % args.build_path)
+ command.extend(lines_by_file.keys())
+ for arg in args.extra_arg:
+ command.append('-extra-arg=%s' % arg)
+ for arg in args.extra_arg_before:
+ command.append('-extra-arg-before=%s' % arg)
+ command.extend(clang_tidy_args)
+
+ sys.exit(subprocess.call(' '.join(command), shell=True))
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+#!/usr/bin/env python
+#
+#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+# FIXME: Integrate with clang-tidy-diff.py
+
+"""
+Parallel clang-tidy runner
+==========================
+
+Runs clang-tidy over all files in a compilation database. Requires clang-tidy
+and clang-apply-replacements in $PATH.
+
+Example invocations.
+- Run clang-tidy on all files in the current working directory with a default
+ set of checks and show warnings in the cpp files and all project headers.
+ run-clang-tidy.py $PWD
+
+- Fix all header guards.
+ run-clang-tidy.py -fix -checks=-*,llvm-header-guard
+
+- Fix all header guards included from clang-tidy and header guards
+ for clang-tidy headers.
+ run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \
+ -header-filter=extra/clang-tidy
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+from __future__ import print_function
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+import traceback
+
+
+def find_compilation_database(path):
+ """Adjusts the directory until a compilation database is found."""
+ result = './'
+ while not os.path.isfile(os.path.join(result, path)):
+ if os.path.realpath(result) == '/':
+ print('Error: could not find compilation database.')
+ sys.exit(1)
+ result += '../'
+ return os.path.realpath(result)
+
+
+def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
+ header_filter, extra_arg, extra_arg_before, quiet):
+ """Gets a command line for clang-tidy."""
+ start = [clang_tidy_binary]
+ if header_filter is not None:
+ start.append('-header-filter=' + header_filter)
+ else:
+ # Show warnings in all in-project headers by default.
+ start.append('-header-filter=^' + build_path + '/.*')
+ if checks:
+ start.append('-checks=' + checks)
+ if tmpdir is not None:
+ start.append('-export-fixes')
+ # Get a temporary file. We immediately close the handle so clang-tidy can
+ # overwrite it.
+ (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
+ os.close(handle)
+ start.append(name)
+ for arg in extra_arg:
+ start.append('-extra-arg=%s' % arg)
+ for arg in extra_arg_before:
+ start.append('-extra-arg-before=%s' % arg)
+ start.append('-p=' + build_path)
+ if quiet:
+ start.append('-quiet')
+ start.append(f)
+ return start
+
+
+def check_clang_apply_replacements_binary(args):
+ """Checks if invoking supplied clang-apply-replacements binary works."""
+ try:
+ subprocess.check_call([args.clang_apply_replacements_binary, '--version'])
+ except:
+ print('Unable to run clang-apply-replacements. Is clang-apply-replacements '
+ 'binary correctly specified?', file=sys.stderr)
+ traceback.print_exc()
+ sys.exit(1)
+
+
+def apply_fixes(args, tmpdir):
+ """Calls clang-apply-fixes on a given directory. Deletes the dir when done."""
+ invocation = [args.clang_apply_replacements_binary]
+ if args.format:
+ invocation.append('-format')
+ if args.style:
+ invocation.append('-style=' + args.style)
+ invocation.append(tmpdir)
+ subprocess.call(invocation)
+
+
+def run_tidy(args, tmpdir, build_path, queue):
+ """Takes filenames out of queue and runs clang-tidy on them."""
+ while True:
+ name = queue.get()
+ invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks,
+ tmpdir, build_path, args.header_filter,
+ args.extra_arg, args.extra_arg_before,
+ args.quiet)
+ sys.stdout.write(' '.join(invocation) + '\n')
+ subprocess.call(invocation)
+ queue.task_done()
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs clang-tidy over all files '
+ 'in a compilation database. Requires '
+ 'clang-tidy and clang-apply-replacements in '
+ '$PATH.')
+ parser.add_argument('-clang-tidy-binary', metavar='PATH',
+ default='clang-tidy',
+ help='path to clang-tidy binary')
+ parser.add_argument('-clang-apply-replacements-binary', metavar='PATH',
+ default='clang-apply-replacements',
+ help='path to clang-apply-replacements binary')
+ parser.add_argument('-checks', default=None,
+ help='checks filter, when not specified, use clang-tidy '
+ 'default')
+ parser.add_argument('-header-filter', default=None,
+ help='regular expression matching the names of the '
+ 'headers to output diagnostics from. Diagnostics from '
+ 'the main file of each translation unit are always '
+ 'displayed.')
+ parser.add_argument('-j', type=int, default=0,
+ help='number of tidy instances to be run in parallel.')
+ parser.add_argument('files', nargs='*', default=['.*'],
+ help='files to be processed (regex on path)')
+ parser.add_argument('-fix', action='store_true', help='apply fix-its')
+ parser.add_argument('-format', action='store_true', help='Reformat code '
+ 'after applying fixes')
+ parser.add_argument('-style', default='file', help='The style of reformat '
+ 'code after applying fixes')
+ parser.add_argument('-p', dest='build_path',
+ help='Path used to read a compile command database.')
+ parser.add_argument('-extra-arg', dest='extra_arg',
+ action='append', default=[],
+ help='Additional argument to append to the compiler '
+ 'command line.')
+ parser.add_argument('-extra-arg-before', dest='extra_arg_before',
+ action='append', default=[],
+ help='Additional argument to prepend to the compiler '
+ 'command line.')
+ parser.add_argument('-quiet', action='store_true',
+ help='Run clang-tidy in quiet mode')
+ args = parser.parse_args()
+
+ db_path = 'compile_commands.json'
+
+ if args.build_path is not None:
+ build_path = args.build_path
+ else:
+ # Find our database
+ build_path = find_compilation_database(db_path)
+
+ try:
+ invocation = [args.clang_tidy_binary, '-list-checks']
+ invocation.append('-p=' + build_path)
+ if args.checks:
+ invocation.append('-checks=' + args.checks)
+ invocation.append('-')
+ print(subprocess.check_output(invocation))
+ except:
+ print("Unable to run clang-tidy.", file=sys.stderr)
+ sys.exit(1)
+
+ # Load the database and extract all files.
+ database = json.load(open(os.path.join(build_path, db_path)))
+ files = [entry['file'] for entry in database]
+
+ max_task = args.j
+ if max_task == 0:
+ max_task = multiprocessing.cpu_count()
+
+ tmpdir = None
+ if args.fix:
+ check_clang_apply_replacements_binary(args)
+ tmpdir = tempfile.mkdtemp()
+
+ # Build up a big regexy filter from all command line arguments.
+ file_name_re = re.compile('|'.join(args.files))
+
+ try:
+ # Spin up a bunch of tidy-launching threads.
+ queue = Queue.Queue(max_task)
+ for _ in range(max_task):
+ t = threading.Thread(target=run_tidy,
+ args=(args, tmpdir, build_path, queue))
+ t.daemon = True
+ t.start()
+
+ # Fill the queue with files.
+ for name in files:
+ if file_name_re.search(name):
+ queue.put(name)
+
+ # Wait for all threads to be done.
+ queue.join()
+
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print('\nCtrl-C detected, goodbye.')
+ if args.fix:
+ shutil.rmtree(tmpdir)
+ os.kill(0, 9)
+
+ if args.fix:
+ print('Applying fixes ...')
+ successfully_applied = False
+
+ try:
+ apply_fixes(args, tmpdir)
+ successfully_applied = True
+ except:
+ print('Error applying fixes.\n', file=sys.stderr)
+ traceback.print_exc()
+
+ shutil.rmtree(tmpdir)
+ if not successfully_applied:
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+//===---------- ASTUtils.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 "ASTUtils.h"
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+using namespace ast_matchers;
+
+const FunctionDecl *getSurroundingFunction(ASTContext &Context,
+ const Stmt &Statement) {
+ return selectFirst<const FunctionDecl>(
+ "function", match(stmt(hasAncestor(functionDecl().bind("function"))),
+ Statement, Context));
+}
+
+bool IsBinaryOrTernary(const Expr *E) {
+ const Expr *E_base = E->IgnoreImpCasts();
+ if (clang::isa<clang::BinaryOperator>(E_base) ||
+ clang::isa<clang::ConditionalOperator>(E_base)) {
+ return true;
+ }
+
+ if (const auto *Operator =
+ clang::dyn_cast<clang::CXXOperatorCallExpr>(E_base)) {
+ return Operator->isInfixBinaryOp();
+ }
+
+ return false;
+}
+
+bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
+ const LangOptions &LangOpts,
+ StringRef FlagName) {
+ // If the Flag is an integer constant, check it.
+ if (isa<IntegerLiteral>(Flags)) {
+ if (!SM.isMacroBodyExpansion(Flags->getLocStart()) &&
+ !SM.isMacroArgExpansion(Flags->getLocStart()))
+ return false;
+
+ // Get the marco name.
+ auto MacroName = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts);
+
+ return MacroName == FlagName;
+ }
+ // If it's a binary OR operation.
+ if (const auto *BO = dyn_cast<BinaryOperator>(Flags))
+ if (BO->getOpcode() == clang::BinaryOperatorKind::BO_Or)
+ return exprHasBitFlagWithSpelling(BO->getLHS()->IgnoreParenCasts(), SM,
+ LangOpts, FlagName) ||
+ exprHasBitFlagWithSpelling(BO->getRHS()->IgnoreParenCasts(), SM,
+ LangOpts, FlagName);
+
+ // Otherwise, assume it has the flag.
+ return true;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---------- ASTUtils.h - clang-tidy -----------------------------------===//
+//
+// 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_ASTUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
+
+#include "clang/AST/AST.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+// Returns the (closest) Function declaration surrounding |Statement| or NULL.
+const FunctionDecl *getSurroundingFunction(ASTContext &Context,
+ const Stmt &Statement);
+// Determine whether Expr is a Binary or Ternary expression.
+bool IsBinaryOrTernary(const Expr *E);
+
+/// Checks whether a macro flag is present in the given argument. Only considers
+/// cases of single match or match in a binary OR expression. For example,
+/// <needed-flag> or <flag> | <needed-flag> | ...
+bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
+ const LangOptions &LangOpts,
+ StringRef FlagName);
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyUtils
+ ASTUtils.cpp
+ DeclRefExprUtils.cpp
+ ExprSequence.cpp
+ FixItHintUtils.cpp
+ HeaderFileExtensionsUtils.cpp
+ HeaderGuard.cpp
+ IncludeInserter.cpp
+ IncludeSorter.cpp
+ LexerUtils.cpp
+ NamespaceAliaser.cpp
+ OptionsUtils.cpp
+ TypeTraits.cpp
+ UsingInserter.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangLex
+ clangTidy
+ )
--- /dev/null
+//===--- DeclRefExprUtils.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 "DeclRefExprUtils.h"
+#include "Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace decl_ref_expr {
+
+using namespace ::clang::ast_matchers;
+using llvm::SmallPtrSet;
+
+namespace {
+
+template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
+ for (const auto &E : S1)
+ if (S2.count(E) == 0)
+ return false;
+ return true;
+}
+
+// Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
+template <typename Node>
+void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
+ SmallPtrSet<const Node *, 16> &Nodes) {
+ for (const auto &Match : Matches)
+ Nodes.insert(Match.getNodeAs<Node>(ID));
+}
+
+} // namespace
+
+// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
+// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
+SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
+ ASTContext &Context) {
+ auto DeclRefToVar =
+ declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
+ auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
+ // Match method call expressions where the variable is referenced as the this
+ // implicit object argument and opertor call expression for member operators
+ // where the variable is the 0-th argument.
+ auto Matches = match(
+ findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
+ cxxOperatorCallExpr(ConstMethodCallee,
+ hasArgument(0, DeclRefToVar))))),
+ Stmt, Context);
+ SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ auto ConstReferenceOrValue =
+ qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
+ unless(anyOf(referenceType(), pointerType()))));
+ auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
+ DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
+ Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ Matches =
+ match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ return DeclRefs;
+}
+
+// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
+// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
+SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
+ ASTContext &Context) {
+ auto DeclRefToVar =
+ declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
+ auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
+ // Match method call expressions where the variable is referenced as the this
+ // implicit object argument and opertor call expression for member operators
+ // where the variable is the 0-th argument.
+ auto Matches =
+ match(decl(forEachDescendant(expr(
+ anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
+ cxxOperatorCallExpr(ConstMethodCallee,
+ hasArgument(0, DeclRefToVar)))))),
+ Decl, Context);
+ SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ auto ConstReferenceOrValue =
+ qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
+ unless(anyOf(referenceType(), pointerType()))));
+ auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
+ DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
+ Matches = match(decl(forEachDescendant(callExpr(UsedAsConstRefOrValueArg))),
+ Decl, Context);
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ Matches =
+ match(decl(forEachDescendant(cxxConstructExpr(UsedAsConstRefOrValueArg))),
+ Decl, Context);
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ return DeclRefs;
+}
+
+bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
+ ASTContext &Context) {
+ // Collect all DeclRefExprs to the loop variable and all CallExprs and
+ // CXXConstructExprs where the loop variable is used as argument to a const
+ // reference parameter.
+ // If the difference is empty it is safe for the loop variable to be a const
+ // reference.
+ auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
+ auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
+ return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
+}
+
+SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
+ auto Matches = match(
+ findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
+ Stmt, Context);
+ SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ return DeclRefs;
+}
+
+SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) {
+ auto Matches = match(
+ decl(forEachDescendant(
+ declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))),
+ Decl, Context);
+ SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+ extractNodesByIdTo(Matches, "declRef", DeclRefs);
+ return DeclRefs;
+}
+
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context) {
+ auto UsedAsConstRefArg = forEachArgumentWithParam(
+ declRefExpr(equalsNode(&DeclRef)),
+ parmVarDecl(hasType(matchers::isReferenceToConst())));
+ auto Matches = match(
+ decl(hasDescendant(
+ cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
+ isCopyConstructor())))
+ .bind("constructExpr"))),
+ Decl, Context);
+ return !Matches.empty();
+}
+
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context) {
+ auto UsedAsConstRefArg = forEachArgumentWithParam(
+ declRefExpr(equalsNode(&DeclRef)),
+ parmVarDecl(hasType(matchers::isReferenceToConst())));
+ auto Matches = match(
+ decl(hasDescendant(
+ cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="),
+ callee(cxxMethodDecl(isCopyAssignmentOperator())))
+ .bind("operatorCallExpr"))),
+ Decl, Context);
+ return !Matches.empty();
+}
+
+} // namespace decl_ref_expr
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DeclRefExprUtils.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_UTILS_DECLREFEXPRUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace decl_ref_expr {
+
+/// \brief Returns true if all ``DeclRefExpr`` to the variable within ``Stmt``
+/// do not modify it.
+///
+/// Returns ``true`` if only const methods or operators are called on the
+/// variable or the variable is a const reference or value argument to a
+/// ``callExpr()``.
+bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
+ ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt``.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl``.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt`` where
+/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
+ ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl`` where
+/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
+ ASTContext &Context);
+
+/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-constructor
+/// call expression within ``Decl``.
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context);
+
+/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-assignment
+/// operator CallExpr within ``Decl``.
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+ ASTContext &Context);
+
+} // namespace decl_ref_expr
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
--- /dev/null
+//===---------- ExprSequence.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 "ExprSequence.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// Returns the Stmt nodes that are parents of 'S', skipping any potential
+// intermediate non-Stmt nodes.
+//
+// In almost all cases, this function returns a single parent or no parents at
+// all.
+//
+// The case that a Stmt has multiple parents is rare but does actually occur in
+// the parts of the AST that we're interested in. Specifically, InitListExpr
+// nodes cause ASTContext::getParent() to return multiple parents for certain
+// nodes in their subtree because RecursiveASTVisitor visits both the syntactic
+// and semantic forms of InitListExpr, and the parent-child relationships are
+// different between the two forms.
+static SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
+ ASTContext *Context) {
+ SmallVector<const Stmt *, 1> Result;
+
+ ASTContext::DynTypedNodeList Parents = Context->getParents(*S);
+
+ SmallVector<ast_type_traits::DynTypedNode, 1> NodesToProcess(Parents.begin(),
+ Parents.end());
+
+ while (!NodesToProcess.empty()) {
+ ast_type_traits::DynTypedNode Node = NodesToProcess.back();
+ NodesToProcess.pop_back();
+
+ if (const auto *S = Node.get<Stmt>()) {
+ Result.push_back(S);
+ } else {
+ Parents = Context->getParents(Node);
+ NodesToProcess.append(Parents.begin(), Parents.end());
+ }
+ }
+
+ return Result;
+}
+
+namespace {
+bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor,
+ ASTContext *Context) {
+ if (Descendant == Ancestor)
+ return true;
+ for (const Stmt *Parent : getParentStmts(Descendant, Context)) {
+ if (isDescendantOrEqual(Parent, Ancestor, Context))
+ return true;
+ }
+
+ return false;
+}
+}
+
+ExprSequence::ExprSequence(const CFG *TheCFG, ASTContext *TheContext)
+ : Context(TheContext) {
+ for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) {
+ SyntheticStmtSourceMap[SyntheticStmt.first] = SyntheticStmt.second;
+ }
+}
+
+bool ExprSequence::inSequence(const Stmt *Before, const Stmt *After) const {
+ Before = resolveSyntheticStmt(Before);
+ After = resolveSyntheticStmt(After);
+
+ // If 'After' is in the subtree of the siblings that follow 'Before' in the
+ // chain of successors, we know that 'After' is sequenced after 'Before'.
+ for (const Stmt *Successor = getSequenceSuccessor(Before); Successor;
+ Successor = getSequenceSuccessor(Successor)) {
+ if (isDescendantOrEqual(After, Successor, Context))
+ return true;
+ }
+
+ // If 'After' is a parent of 'Before' or is sequenced after one of these
+ // parents, we know that it is sequenced after 'Before'.
+ for (const Stmt *Parent : getParentStmts(Before, Context)) {
+ if (Parent == After || inSequence(Parent, After))
+ return true;
+ }
+
+ return false;
+}
+
+bool ExprSequence::potentiallyAfter(const Stmt *After,
+ const Stmt *Before) const {
+ return !inSequence(After, Before);
+}
+
+const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const {
+ for (const Stmt *Parent : getParentStmts(S, Context)) {
+ if (const auto *BO = dyn_cast<BinaryOperator>(Parent)) {
+ // Comma operator: Right-hand side is sequenced after the left-hand side.
+ if (BO->getLHS() == S && BO->getOpcode() == BO_Comma)
+ return BO->getRHS();
+ } else if (const auto *InitList = dyn_cast<InitListExpr>(Parent)) {
+ // Initializer list: Each initializer clause is sequenced after the
+ // clauses that precede it.
+ for (unsigned I = 1; I < InitList->getNumInits(); ++I) {
+ if (InitList->getInit(I - 1) == S)
+ return InitList->getInit(I);
+ }
+ } else if (const auto *Compound = dyn_cast<CompoundStmt>(Parent)) {
+ // Compound statement: Each sub-statement is sequenced after the
+ // statements that precede it.
+ const Stmt *Previous = nullptr;
+ for (const auto *Child : Compound->body()) {
+ if (Previous == S)
+ return Child;
+ Previous = Child;
+ }
+ } else if (const auto *TheDeclStmt = dyn_cast<DeclStmt>(Parent)) {
+ // Declaration: Every initializer expression is sequenced after the
+ // initializer expressions that precede it.
+ const Expr *PreviousInit = nullptr;
+ for (const Decl *TheDecl : TheDeclStmt->decls()) {
+ if (const auto *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
+ if (const Expr *Init = TheVarDecl->getInit()) {
+ if (PreviousInit == S)
+ return Init;
+ PreviousInit = Init;
+ }
+ }
+ }
+ } else if (const auto *ForRange = dyn_cast<CXXForRangeStmt>(Parent)) {
+ // Range-based for: Loop variable declaration is sequenced before the
+ // body. (We need this rule because these get placed in the same
+ // CFGBlock.)
+ if (S == ForRange->getLoopVarStmt())
+ return ForRange->getBody();
+ } else if (const auto *TheIfStmt = dyn_cast<IfStmt>(Parent)) {
+ // If statement: If a variable is declared inside the condition, the
+ // expression used to initialize the variable is sequenced before the
+ // evaluation of the condition.
+ if (S == TheIfStmt->getConditionVariableDeclStmt())
+ return TheIfStmt->getCond();
+ }
+ }
+
+ return nullptr;
+}
+
+const Stmt *ExprSequence::resolveSyntheticStmt(const Stmt *S) const {
+ if (SyntheticStmtSourceMap.count(S))
+ return SyntheticStmtSourceMap.lookup(S);
+ return S;
+}
+
+StmtToBlockMap::StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
+ : Context(TheContext) {
+ for (const auto *B : *TheCFG) {
+ for (const auto &Elem : *B) {
+ if (Optional<CFGStmt> S = Elem.getAs<CFGStmt>())
+ Map[S->getStmt()] = B;
+ }
+ }
+}
+
+const CFGBlock *StmtToBlockMap::blockContainingStmt(const Stmt *S) const {
+ while (!Map.count(S)) {
+ SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
+ if (Parents.empty())
+ return nullptr;
+ S = Parents[0];
+ }
+
+ return Map.lookup(S);
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===------------- ExprSequence.h - clang-tidy ----------------------------===//
+//
+// 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_EXPRSEQUENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
+
+#include "clang/Analysis/CFG.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Provides information about the evaluation order of (sub-)expressions within
+/// a `CFGBlock`.
+///
+/// While a `CFGBlock` does contain individual `CFGElement`s for some
+/// sub-expressions, the order in which those `CFGElement`s appear reflects
+/// only one possible order in which the sub-expressions may be evaluated.
+/// However, we want to warn if any of the potential evaluation orders can lead
+/// to a use-after-move, not just the one contained in the `CFGBlock`.
+///
+/// This class implements only a simplified version of the C++ sequencing
+/// rules. The main limitation is that we do not distinguish between value
+/// computation and side effect -- see the "Implementation" section for more
+/// details.
+///
+/// Note: `SequenceChecker` from SemaChecking.cpp does a similar job (and much
+/// more thoroughly), but using it would require
+/// - Pulling `SequenceChecker` out into a header file (i.e. making it part of
+/// the API),
+/// - Removing the dependency of `SequenceChecker` on `Sema`, and
+/// - (Probably) modifying `SequenceChecker` to make it suitable to be used in
+/// this context.
+/// For the moment, it seems preferable to re-implement our own version of
+/// sequence checking that is special-cased to what we need here.
+///
+/// Implementation
+/// --------------
+///
+/// `ExprSequence` uses two types of sequencing edges between nodes in the AST:
+///
+/// - Every `Stmt` is assumed to be sequenced after its children. This is
+/// overly optimistic because the standard only states that value computations
+/// of operands are sequenced before the value computation of the operator,
+/// making no guarantees about side effects (in general).
+///
+/// For our purposes, this rule is sufficient, however, because this check is
+/// interested in operations on objects, which are generally performed through
+/// function calls (whether explicit and implicit). Function calls guarantee
+/// that the value computations and side effects for all function arguments
+/// are sequenced before the execution of the function.
+///
+/// - In addition, some `Stmt`s are known to be sequenced before or after
+/// their siblings. For example, the `Stmt`s that make up a `CompoundStmt`are
+/// all sequenced relative to each other. The function
+/// `getSequenceSuccessor()` implements these sequencing rules.
+class ExprSequence {
+public:
+ /// Initializes this `ExprSequence` with sequence information for the given
+ /// `CFG`.
+ ExprSequence(const CFG *TheCFG, ASTContext *TheContext);
+
+ /// Returns whether \p Before is sequenced before \p After.
+ bool inSequence(const Stmt *Before, const Stmt *After) const;
+
+ /// Returns whether \p After can potentially be evaluated after \p Before.
+ /// This is exactly equivalent to `!inSequence(After, Before)` but makes some
+ /// conditions read more naturally.
+ bool potentiallyAfter(const Stmt *After, const Stmt *Before) const;
+
+private:
+ // Returns the sibling of \p S (if any) that is directly sequenced after \p S,
+ // or nullptr if no such sibling exists. For example, if \p S is the child of
+ // a `CompoundStmt`, this would return the Stmt that directly follows \p S in
+ // the `CompoundStmt`.
+ //
+ // As the sequencing of many constructs that change control flow is already
+ // encoded in the `CFG`, this function only implements the sequencing rules
+ // for those constructs where sequencing cannot be inferred from the `CFG`.
+ const Stmt *getSequenceSuccessor(const Stmt *S) const;
+
+ const Stmt *resolveSyntheticStmt(const Stmt *S) const;
+
+ ASTContext *Context;
+
+ llvm::DenseMap<const Stmt *, const Stmt *> SyntheticStmtSourceMap;
+};
+
+/// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be
+/// contained in more than one `CFGBlock`; in this case, they are mapped to the
+/// innermost block (i.e. the one that is furthest from the root of the tree).
+class StmtToBlockMap {
+public:
+ /// Initializes the map for the given `CFG`.
+ StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext);
+
+ /// Returns the block that \p S is contained in. Some `Stmt`s may be contained
+ /// in more than one `CFGBlock`; in this case, this function returns the
+ /// innermost block (i.e. the one that is furthest from the root of the tree).
+ const CFGBlock *blockContainingStmt(const Stmt *S) const;
+
+private:
+ ASTContext *Context;
+
+ llvm::DenseMap<const Stmt *, const CFGBlock *> Map;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
--- /dev/null
+//===--- FixItHintUtils.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 "FixItHintUtils.h"
+#include "LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace fixit {
+
+FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
+ SourceLocation AmpLocation = Var.getLocation();
+ auto Token = utils::lexer::getPreviousToken(Context, AmpLocation);
+ if (!Token.is(tok::unknown))
+ AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
+ Context.getSourceManager(),
+ Context.getLangOpts());
+ return FixItHint::CreateInsertion(AmpLocation, "&");
+}
+
+FixItHint changeVarDeclToConst(const VarDecl &Var) {
+ return FixItHint::CreateInsertion(Var.getTypeSpecStartLoc(), "const ");
+}
+
+} // namespace fixit
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- FixItHintUtils.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_UTILS_FIXITHINTUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace fixit {
+
+/// \brief Creates fix to make ``VarDecl`` a reference by adding ``&``.
+FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context);
+
+/// \brief Creates fix to make ``VarDecl`` const qualified.
+FixItHint changeVarDeclToConst(const VarDecl &Var);
+
+} // namespace fixit
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
--- /dev/null
+//===--- HeaderFileExtensionsUtils.cpp - clang-tidy--------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderFileExtensionsUtils.h"
+#include "clang/Basic/CharInfo.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+bool isExpansionLocInHeaderFile(
+ SourceLocation Loc, const SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions) {
+ SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
+ return isHeaderFileExtension(SM.getFilename(ExpansionLoc),
+ HeaderFileExtensions);
+}
+
+bool isPresumedLocInHeaderFile(
+ SourceLocation Loc, SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions) {
+ PresumedLoc PresumedLocation = SM.getPresumedLoc(Loc);
+ return isHeaderFileExtension(PresumedLocation.getFilename(),
+ HeaderFileExtensions);
+}
+
+bool isSpellingLocInHeaderFile(
+ SourceLocation Loc, SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions) {
+ SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
+ return isHeaderFileExtension(SM.getFilename(SpellingLoc),
+ HeaderFileExtensions);
+}
+
+bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
+ HeaderFileExtensionsSet &HeaderFileExtensions,
+ char delimiter) {
+ SmallVector<StringRef, 5> Suffixes;
+ AllHeaderFileExtensions.split(Suffixes, delimiter);
+ HeaderFileExtensions.clear();
+ for (StringRef Suffix : Suffixes) {
+ StringRef Extension = Suffix.trim();
+ for (StringRef::const_iterator it = Extension.begin();
+ it != Extension.end(); ++it) {
+ if (!isAlphanumeric(*it))
+ return false;
+ }
+ HeaderFileExtensions.insert(Extension);
+ }
+ return true;
+}
+
+bool isHeaderFileExtension(
+ StringRef FileName, const HeaderFileExtensionsSet &HeaderFileExtensions) {
+ StringRef extension = llvm::sys::path::extension(FileName);
+ if (extension.empty())
+ return false;
+ // Skip "." prefix.
+ return HeaderFileExtensions.count(extension.substr(1)) > 0;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- HeaderFileExtensionsUtils.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_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+typedef llvm::SmallSet<llvm::StringRef, 5> HeaderFileExtensionsSet;
+
+/// \brief Checks whether expansion location of \p Loc is in header file.
+bool isExpansionLocInHeaderFile(
+ SourceLocation Loc, const SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Checks whether presumed location of \p Loc is in header file.
+bool isPresumedLocInHeaderFile(
+ SourceLocation Loc, SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Checks whether spelling location of \p Loc is in header file.
+bool isSpellingLocInHeaderFile(
+ SourceLocation Loc, SourceManager &SM,
+ const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Parses header file extensions from a semicolon-separated list.
+bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
+ HeaderFileExtensionsSet &HeaderFileExtensions,
+ char delimiter);
+
+/// \brief Decides whether a file has a header file extension.
+bool isHeaderFileExtension(StringRef FileName,
+ const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
--- /dev/null
+//===--- HeaderGuard.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 "HeaderGuard.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// \brief canonicalize a path by removing ./ and ../ components.
+static std::string cleanPath(StringRef Path) {
+ SmallString<256> Result = Path;
+ llvm::sys::path::remove_dots(Result, true);
+ return Result.str();
+}
+
+namespace {
+class HeaderGuardPPCallbacks : public PPCallbacks {
+public:
+ HeaderGuardPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check)
+ : PP(PP), Check(Check) {}
+
+ void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::CharacteristicKind FileType,
+ FileID PrevFID) override {
+ // Record all files we enter. We'll need them to diagnose headers without
+ // guards.
+ SourceManager &SM = PP->getSourceManager();
+ if (Reason == EnterFile && FileType == SrcMgr::C_User) {
+ if (const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
+ std::string FileName = cleanPath(FE->getName());
+ Files[FileName] = FE;
+ }
+ }
+ }
+
+ void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) override {
+ if (MD)
+ return;
+
+ // Record #ifndefs that succeeded. We also need the Location of the Name.
+ Ifndefs[MacroNameTok.getIdentifierInfo()] =
+ std::make_pair(Loc, MacroNameTok.getLocation());
+ }
+
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override {
+ // Record all defined macros. We store the whole token to get info on the
+ // name later.
+ Macros.emplace_back(MacroNameTok, MD->getMacroInfo());
+ }
+
+ void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
+ // Record all #endif and the corresponding #ifs (including #ifndefs).
+ EndIfs[IfLoc] = Loc;
+ }
+
+ void EndOfMainFile() override {
+ // Now that we have all this information from the preprocessor, use it!
+ SourceManager &SM = PP->getSourceManager();
+
+ for (const auto &MacroEntry : Macros) {
+ const MacroInfo *MI = MacroEntry.second;
+
+ // We use clang's header guard detection. This has the advantage of also
+ // emitting a warning for cases where a pseudo header guard is found but
+ // preceded by something blocking the header guard optimization.
+ if (!MI->isUsedForHeaderGuard())
+ continue;
+
+ const FileEntry *FE =
+ SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
+ std::string FileName = cleanPath(FE->getName());
+ Files.erase(FileName);
+
+ // See if we should check and fix this header guard.
+ if (!Check->shouldFixHeaderGuard(FileName))
+ continue;
+
+ // Look up Locations for this guard.
+ SourceLocation Ifndef =
+ Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
+ SourceLocation Define = MacroEntry.first.getLocation();
+ SourceLocation EndIf =
+ EndIfs[Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
+
+ // If the macro Name is not equal to what we can compute, correct it in
+ // the #ifndef and #define.
+ StringRef CurHeaderGuard =
+ MacroEntry.first.getIdentifierInfo()->getName();
+ std::vector<FixItHint> FixIts;
+ std::string NewGuard = checkHeaderGuardDefinition(
+ Ifndef, Define, EndIf, FileName, CurHeaderGuard, FixIts);
+
+ // Now look at the #endif. We want a comment with the header guard. Fix it
+ // at the slightest deviation.
+ checkEndifComment(FileName, EndIf, NewGuard, FixIts);
+
+ // Bundle all fix-its into one warning. The message depends on whether we
+ // changed the header guard or not.
+ if (!FixIts.empty()) {
+ if (CurHeaderGuard != NewGuard) {
+ Check->diag(Ifndef, "header guard does not follow preferred style")
+ << FixIts;
+ } else {
+ Check->diag(EndIf, "#endif for a header guard should reference the "
+ "guard macro in a comment")
+ << FixIts;
+ }
+ }
+ }
+
+ // Emit warnings for headers that are missing guards.
+ checkGuardlessHeaders();
+
+ // Clear all state.
+ Macros.clear();
+ Files.clear();
+ Ifndefs.clear();
+ EndIfs.clear();
+ }
+
+ bool wouldFixEndifComment(StringRef FileName, SourceLocation EndIf,
+ StringRef HeaderGuard,
+ size_t *EndIfLenPtr = nullptr) {
+ if (!EndIf.isValid())
+ return false;
+ const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
+ size_t EndIfLen = std::strcspn(EndIfData, "\r\n");
+ if (EndIfLenPtr)
+ *EndIfLenPtr = EndIfLen;
+
+ StringRef EndIfStr(EndIfData, EndIfLen);
+ EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of("#endif \t"));
+
+ // Give up if there's an escaped newline.
+ size_t FindEscapedNewline = EndIfStr.find_last_not_of(' ');
+ if (FindEscapedNewline != StringRef::npos &&
+ EndIfStr[FindEscapedNewline] == '\\')
+ return false;
+
+ if (!Check->shouldSuggestEndifComment(FileName) &&
+ !(EndIfStr.startswith("//") ||
+ (EndIfStr.startswith("/*") && EndIfStr.endswith("*/"))))
+ return false;
+
+ return (EndIfStr != "// " + HeaderGuard.str()) &&
+ (EndIfStr != "/* " + HeaderGuard.str() + " */");
+ }
+
+ /// \brief Look for header guards that don't match the preferred style. Emit
+ /// fix-its and return the suggested header guard (or the original if no
+ /// change was made.
+ std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
+ SourceLocation Define,
+ SourceLocation EndIf,
+ StringRef FileName,
+ StringRef CurHeaderGuard,
+ std::vector<FixItHint> &FixIts) {
+ std::string CPPVar = Check->getHeaderGuard(FileName, CurHeaderGuard);
+ std::string CPPVarUnder = CPPVar + '_';
+
+ // Allow a trailing underscore iff we don't have to change the endif comment
+ // too.
+ if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
+ (CurHeaderGuard != CPPVarUnder ||
+ wouldFixEndifComment(FileName, EndIf, CurHeaderGuard))) {
+ FixIts.push_back(FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(
+ Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
+ CPPVar));
+ FixIts.push_back(FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(
+ Define, Define.getLocWithOffset(CurHeaderGuard.size())),
+ CPPVar));
+ return CPPVar;
+ }
+ return CurHeaderGuard;
+ }
+
+ /// \brief Checks the comment after the #endif of a header guard and fixes it
+ /// if it doesn't match \c HeaderGuard.
+ void checkEndifComment(StringRef FileName, SourceLocation EndIf,
+ StringRef HeaderGuard,
+ std::vector<FixItHint> &FixIts) {
+ size_t EndIfLen;
+ if (wouldFixEndifComment(FileName, EndIf, HeaderGuard, &EndIfLen)) {
+ FixIts.push_back(FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(EndIf,
+ EndIf.getLocWithOffset(EndIfLen)),
+ Check->formatEndIf(HeaderGuard)));
+ }
+ }
+
+ /// \brief Looks for files that were visited but didn't have a header guard.
+ /// Emits a warning with fixits suggesting adding one.
+ void checkGuardlessHeaders() {
+ // Look for header files that didn't have a header guard. Emit a warning and
+ // fix-its to add the guard.
+ // TODO: Insert the guard after top comments.
+ for (const auto &FE : Files) {
+ StringRef FileName = FE.getKey();
+ if (!Check->shouldSuggestToAddHeaderGuard(FileName))
+ continue;
+
+ SourceManager &SM = PP->getSourceManager();
+ FileID FID = SM.translateFile(FE.getValue());
+ SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
+ if (StartLoc.isInvalid())
+ continue;
+
+ std::string CPPVar = Check->getHeaderGuard(FileName);
+ std::string CPPVarUnder = CPPVar + '_'; // Allow a trailing underscore.
+ // If there's a macro with a name that follows the header guard convention
+ // but was not recognized by the preprocessor as a header guard there must
+ // be code outside of the guarded area. Emit a plain warning without
+ // fix-its.
+ // FIXME: Can we move it into the right spot?
+ bool SeenMacro = false;
+ for (const auto &MacroEntry : Macros) {
+ StringRef Name = MacroEntry.first.getIdentifierInfo()->getName();
+ SourceLocation DefineLoc = MacroEntry.first.getLocation();
+ if ((Name == CPPVar || Name == CPPVarUnder) &&
+ SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
+ Check->diag(DefineLoc, "code/includes outside of area guarded by "
+ "header guard; consider moving it");
+ SeenMacro = true;
+ break;
+ }
+ }
+
+ if (SeenMacro)
+ continue;
+
+ Check->diag(StartLoc, "header is missing header guard")
+ << FixItHint::CreateInsertion(
+ StartLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + "\n\n")
+ << FixItHint::CreateInsertion(
+ SM.getLocForEndOfFile(FID),
+ Check->shouldSuggestEndifComment(FileName)
+ ? "\n#" + Check->formatEndIf(CPPVar) + "\n"
+ : "\n#endif\n");
+ }
+ }
+
+private:
+ std::vector<std::pair<Token, const MacroInfo *>> Macros;
+ llvm::StringMap<const FileEntry *> Files;
+ std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
+ Ifndefs;
+ std::map<SourceLocation, SourceLocation> EndIfs;
+
+ Preprocessor *PP;
+ HeaderGuardCheck *Check;
+};
+} // namespace
+
+void HeaderGuardCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+ Compiler.getPreprocessor().addPPCallbacks(
+ llvm::make_unique<HeaderGuardPPCallbacks>(&Compiler.getPreprocessor(),
+ this));
+}
+
+bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef FileName) {
+ return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+bool HeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) { return true; }
+
+bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef FileName) {
+ return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+std::string HeaderGuardCheck::formatEndIf(StringRef HeaderGuard) {
+ return "endif // " + HeaderGuard.str();
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- HeaderGuard.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_UTILS_HEADERGUARD_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Finds and fixes header guards.
+/// The check supports these options:
+/// - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+/// header files (The filename extension should not contain "." prefix).
+/// ",h,hh,hpp,hxx" by default.
+/// For extension-less header files, using an empty string or leaving an
+/// empty string between "," if there are other filename extensions.
+class HeaderGuardCheck : public ClangTidyCheck {
+public:
+ HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ RawStringHeaderFileExtensions(
+ Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+ utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+ HeaderFileExtensions, ',');
+ }
+ void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+ /// Returns ``true`` if the check should suggest inserting a trailing comment
+ /// on the ``#endif`` of the header guard. It will use the same name as
+ /// returned by ``HeaderGuardCheck::getHeaderGuard``.
+ virtual bool shouldSuggestEndifComment(StringRef Filename);
+ /// Returns ``true`` if the check should suggest changing an existing header
+ /// guard to the string returned by ``HeaderGuardCheck::getHeaderGuard``.
+ virtual bool shouldFixHeaderGuard(StringRef Filename);
+ /// Returns ``true`` if the check should add a header guard to the file
+ /// if it has none.
+ virtual bool shouldSuggestToAddHeaderGuard(StringRef Filename);
+ /// Returns a replacement for the ``#endif`` line with a comment mentioning
+ /// \p HeaderGuard. The replacement should start with ``endif``.
+ virtual std::string formatEndIf(StringRef HeaderGuard);
+ /// Gets the canonical header guard for a file.
+ virtual std::string getHeaderGuard(StringRef Filename,
+ StringRef OldGuard = StringRef()) = 0;
+
+private:
+ std::string RawStringHeaderFileExtensions;
+ utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
--- /dev/null
+//===-------- IncludeInserter.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 "IncludeInserter.h"
+#include "clang/Lex/Token.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+class IncludeInserterCallback : public PPCallbacks {
+public:
+ explicit IncludeInserterCallback(IncludeInserter *Inserter)
+ : Inserter(Inserter) {}
+ // Implements PPCallbacks::InclusionDerective(). Records the names and source
+ // locations of the inclusions in the main source file being processed.
+ void InclusionDirective(SourceLocation HashLocation,
+ const Token &IncludeToken, StringRef FileNameRef,
+ bool IsAngled, CharSourceRange FileNameRange,
+ const FileEntry * /*IncludedFile*/,
+ StringRef /*SearchPath*/, StringRef /*RelativePath*/,
+ const Module * /*ImportedModule*/) override {
+ Inserter->AddInclude(FileNameRef, IsAngled, HashLocation,
+ IncludeToken.getEndLoc());
+ }
+
+private:
+ IncludeInserter *Inserter;
+};
+
+IncludeInserter::IncludeInserter(const SourceManager &SourceMgr,
+ const LangOptions &LangOpts,
+ IncludeSorter::IncludeStyle Style)
+ : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style) {}
+
+IncludeInserter::~IncludeInserter() {}
+
+std::unique_ptr<PPCallbacks> IncludeInserter::CreatePPCallbacks() {
+ return llvm::make_unique<IncludeInserterCallback>(this);
+}
+
+llvm::Optional<FixItHint>
+IncludeInserter::CreateIncludeInsertion(FileID FileID, StringRef Header,
+ bool IsAngled) {
+ // We assume the same Header will never be included both angled and not
+ // angled.
+ if (!InsertedHeaders[FileID].insert(Header).second)
+ return llvm::None;
+
+ if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
+ // This may happen if there have been no preprocessor directives in this
+ // file.
+ IncludeSorterByFile.insert(std::make_pair(
+ FileID,
+ llvm::make_unique<IncludeSorter>(
+ &SourceMgr, &LangOpts, FileID,
+ SourceMgr.getFilename(SourceMgr.getLocForStartOfFile(FileID)),
+ Style)));
+ }
+ return IncludeSorterByFile[FileID]->CreateIncludeInsertion(Header, IsAngled);
+}
+
+void IncludeInserter::AddInclude(StringRef FileName, bool IsAngled,
+ SourceLocation HashLocation,
+ SourceLocation EndLocation) {
+ FileID FileID = SourceMgr.getFileID(HashLocation);
+ if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
+ IncludeSorterByFile.insert(std::make_pair(
+ FileID, llvm::make_unique<IncludeSorter>(
+ &SourceMgr, &LangOpts, FileID,
+ SourceMgr.getFilename(HashLocation), Style)));
+ }
+ IncludeSorterByFile[FileID]->AddInclude(FileName, IsAngled, HashLocation,
+ EndLocation);
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---------- IncludeInserter.h - clang-tidy ----------------------------===//
+//
+// 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_INCLUDEINSERTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
+
+#include "IncludeSorter.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/PPCallbacks.h"
+#include <memory>
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// \brief Produces fixes to insert specified includes to source files, if not
+/// yet present.
+///
+/// ``IncludeInserter`` can be used by ``ClangTidyCheck`` in the following
+/// fashion:
+/// \code
+/// class MyCheck : public ClangTidyCheck {
+/// public:
+/// void registerPPCallbacks(CompilerInstance& Compiler) override {
+/// Inserter.reset(new IncludeInserter(&Compiler.getSourceManager(),
+/// &Compiler.getLangOpts()));
+/// Compiler.getPreprocessor().addPPCallbacks(
+/// Inserter->CreatePPCallback());
+/// }
+///
+/// void registerMatchers(ast_matchers::MatchFinder* Finder) override { ... }
+///
+/// void check(
+/// const ast_matchers::MatchFinder::MatchResult& Result) override {
+/// ...
+/// Inserter->CreateIncludeInsertion(
+/// Result.SourceManager->getMainFileID(), "path/to/Header.h",
+/// /*IsAngled=*/false);
+/// ...
+/// }
+///
+/// private:
+/// std::unique_ptr<IncludeInserter> Inserter;
+/// };
+/// \endcode
+class IncludeInserter {
+public:
+ IncludeInserter(const SourceManager &SourceMgr, const LangOptions &LangOpts,
+ IncludeSorter::IncludeStyle Style);
+ ~IncludeInserter();
+
+ /// Create ``PPCallbacks`` for registration with the compiler's preprocessor.
+ std::unique_ptr<PPCallbacks> CreatePPCallbacks();
+
+ /// Creates a \p Header inclusion directive fixit. Returns ``llvm::None`` on
+ /// error or if inclusion directive already exists.
+ llvm::Optional<FixItHint>
+ CreateIncludeInsertion(FileID FileID, llvm::StringRef Header, bool IsAngled);
+
+private:
+ void AddInclude(StringRef FileName, bool IsAngled,
+ SourceLocation HashLocation, SourceLocation EndLocation);
+
+ llvm::DenseMap<FileID, std::unique_ptr<IncludeSorter>> IncludeSorterByFile;
+ llvm::DenseMap<FileID, std::set<std::string>> InsertedHeaders;
+ const SourceManager &SourceMgr;
+ const LangOptions &LangOpts;
+ const IncludeSorter::IncludeStyle Style;
+ friend class IncludeInserterCallback;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
--- /dev/null
+//===---------- IncludeSorter.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 "IncludeSorter.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+namespace {
+
+StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
+ for (StringRef Suffix : Suffixes) {
+ if (Str.endswith(Suffix)) {
+ return Str.substr(0, Str.size() - Suffix.size());
+ }
+ }
+ return Str;
+}
+
+StringRef MakeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
+ // The list of suffixes to remove from source file names to get the
+ // "canonical" file names.
+ // E.g. tools/sort_includes.cc and tools/sort_includes_test.cc
+ // would both canonicalize to tools/sort_includes and tools/sort_includes.h
+ // (once canonicalized) will match as being the main include file associated
+ // with the source files.
+ if (Style == IncludeSorter::IS_LLVM) {
+ return RemoveFirstSuffix(
+ RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
+ }
+ return RemoveFirstSuffix(
+ RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
+ {"_unittest", "_regtest", "_test"});
+}
+
+// Scan to the end of the line and return the offset of the next line.
+size_t FindNextLine(const char *Text) {
+ size_t EOLIndex = std::strcspn(Text, "\n");
+ return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1;
+}
+
+IncludeSorter::IncludeKinds
+DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
+ bool IsAngled, IncludeSorter::IncludeStyle Style) {
+ // Compute the two "canonical" forms of the include's filename sans extension.
+ // The first form is the include's filename without ".h" or "-inl.h" at the
+ // end. The second form is the first form with "/public/" in the file path
+ // replaced by "/internal/".
+ if (IsAngled) {
+ // If the system include (<foo>) ends with ".h", then it is a normal C-style
+ // include. Otherwise assume it is a C++-style extensionless include.
+ return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude
+ : IncludeSorter::IK_CXXSystemInclude;
+ }
+ StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
+ if (CanonicalFile.endswith(CanonicalInclude)
+ || CanonicalInclude.endswith(CanonicalFile)) {
+ return IncludeSorter::IK_MainTUInclude;
+ }
+ if (Style == IncludeSorter::IS_Google) {
+ std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
+ std::string AltCanonicalInclude =
+ Parts.first.str() + "/internal/" + Parts.second.str();
+ std::string ProtoCanonicalInclude =
+ Parts.first.str() + "/proto/" + Parts.second.str();
+
+ // Determine the kind of this inclusion.
+ if (CanonicalFile.equals(AltCanonicalInclude) ||
+ CanonicalFile.equals(ProtoCanonicalInclude)) {
+ return IncludeSorter::IK_MainTUInclude;
+ }
+ }
+ return IncludeSorter::IK_NonSystemInclude;
+}
+
+} // namespace
+
+IncludeSorter::IncludeSorter(const SourceManager *SourceMgr,
+ const LangOptions *LangOpts, const FileID FileID,
+ StringRef FileName, IncludeStyle Style)
+ : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
+ CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
+}
+
+void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled,
+ SourceLocation HashLocation,
+ SourceLocation EndLocation) {
+ int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
+
+ // Record the relevant location information for this inclusion directive.
+ IncludeLocations[FileName].push_back(
+ SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
+ SourceLocations.push_back(IncludeLocations[FileName].back());
+
+ // Stop if this inclusion is a duplicate.
+ if (IncludeLocations[FileName].size() > 1)
+ return;
+
+ // Add the included file's name to the appropriate bucket.
+ IncludeKinds Kind =
+ DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
+ if (Kind != IK_InvalidInclude)
+ IncludeBucket[Kind].push_back(FileName.str());
+}
+
+Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
+ bool IsAngled) {
+ std::string IncludeStmt =
+ IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
+ : llvm::Twine("#include \"" + FileName + "\"\n").str();
+ if (SourceLocations.empty()) {
+ // If there are no includes in this file, add it in the first line.
+ // FIXME: insert after the file comment or the header guard, if present.
+ IncludeStmt.append("\n");
+ return FixItHint::CreateInsertion(
+ SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
+ }
+
+ auto IncludeKind =
+ DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
+
+ if (!IncludeBucket[IncludeKind].empty()) {
+ for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
+ if (FileName < IncludeEntry) {
+ const auto &Location = IncludeLocations[IncludeEntry][0];
+ return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
+ } else if (FileName == IncludeEntry) {
+ return llvm::None;
+ }
+ }
+ // FileName comes after all include entries in bucket, insert it after
+ // last.
+ const std::string &LastInclude = IncludeBucket[IncludeKind].back();
+ SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
+ return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
+ IncludeStmt);
+ }
+ // Find the non-empty include bucket to be sorted directly above
+ // 'IncludeKind'. If such a bucket exists, we'll want to sort the include
+ // after that bucket. If no such bucket exists, find the first non-empty
+ // include bucket in the file. In that case, we'll want to sort the include
+ // before that bucket.
+ IncludeKinds NonEmptyKind = IK_InvalidInclude;
+ for (int i = IK_InvalidInclude - 1; i >= 0; --i) {
+ if (!IncludeBucket[i].empty()) {
+ NonEmptyKind = static_cast<IncludeKinds>(i);
+ if (NonEmptyKind < IncludeKind)
+ break;
+ }
+ }
+ if (NonEmptyKind == IK_InvalidInclude) {
+ return llvm::None;
+ }
+
+ if (NonEmptyKind < IncludeKind) {
+ // Create a block after.
+ const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
+ SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
+ IncludeStmt = '\n' + IncludeStmt;
+ return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
+ IncludeStmt);
+ }
+ // Create a block before.
+ const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
+ SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
+ IncludeStmt.append("\n");
+ return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
+ IncludeStmt);
+}
+
+std::vector<FixItHint> IncludeSorter::GetEdits() {
+ if (SourceLocations.empty())
+ return {};
+
+ typedef std::map<int, std::pair<SourceRange, std::string>>
+ FileLineToSourceEditMap;
+ FileLineToSourceEditMap Edits;
+ auto SourceLocationIterator = SourceLocations.begin();
+ auto SourceLocationIteratorEnd = SourceLocations.end();
+
+ // Compute the Edits that need to be done to each line to add, replace, or
+ // delete inclusions.
+ for (int IncludeKind = 0; IncludeKind < IK_InvalidInclude; ++IncludeKind) {
+ std::sort(IncludeBucket[IncludeKind].begin(),
+ IncludeBucket[IncludeKind].end());
+ for (const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
+ auto &Location = IncludeLocations[IncludeEntry];
+ SourceRangeVector::iterator LocationIterator = Location.begin();
+ SourceRangeVector::iterator LocationIteratorEnd = Location.end();
+ SourceRange FirstLocation = *LocationIterator;
+
+ // If the first occurrence of a particular include is on the current
+ // source line we are examining, leave it alone.
+ if (FirstLocation == *SourceLocationIterator)
+ ++LocationIterator;
+
+ // Add the deletion Edits for any (remaining) instances of this inclusion,
+ // and remove their Locations from the source Locations to be processed.
+ for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
+ int LineNumber =
+ SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
+ Edits[LineNumber] = std::make_pair(*LocationIterator, "");
+ SourceLocationIteratorEnd =
+ std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
+ *LocationIterator);
+ }
+
+ if (FirstLocation == *SourceLocationIterator) {
+ // Do nothing except move to the next source Location (Location of an
+ // inclusion in the original, unchanged source file).
+ ++SourceLocationIterator;
+ continue;
+ }
+
+ // Add (or append to) the replacement text for this line in source file.
+ int LineNumber =
+ SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
+ if (Edits.find(LineNumber) == Edits.end()) {
+ Edits[LineNumber].first =
+ SourceRange(SourceLocationIterator->getBegin());
+ }
+ StringRef SourceText = Lexer::getSourceText(
+ CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
+ Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
+ }
+
+ // Clear the bucket.
+ IncludeBucket[IncludeKind].clear();
+ }
+
+ // Go through the single-line Edits and combine them into blocks of Edits.
+ int CurrentEndLine = 0;
+ SourceRange CurrentRange;
+ std::string CurrentText;
+ std::vector<FixItHint> Fixes;
+ for (const auto &LineEdit : Edits) {
+ // If the current edit is on the next line after the previous edit, add it
+ // to the current block edit.
+ if (LineEdit.first == CurrentEndLine + 1 &&
+ CurrentRange.getBegin() != CurrentRange.getEnd()) {
+ SourceRange EditRange = LineEdit.second.first;
+ if (EditRange.getBegin() != EditRange.getEnd()) {
+ ++CurrentEndLine;
+ CurrentRange.setEnd(EditRange.getEnd());
+ }
+ CurrentText += LineEdit.second.second;
+ // Otherwise report the current block edit and start a new block.
+ } else {
+ if (CurrentEndLine) {
+ Fixes.push_back(FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(CurrentRange), CurrentText));
+ }
+
+ CurrentEndLine = LineEdit.first;
+ CurrentRange = LineEdit.second.first;
+ CurrentText = LineEdit.second.second;
+ }
+ }
+ // Finally, report the current block edit if there is one.
+ if (CurrentEndLine) {
+ Fixes.push_back(FixItHint::CreateReplacement(
+ CharSourceRange::getCharRange(CurrentRange), CurrentText));
+ }
+
+ // Reset the remaining internal state.
+ SourceLocations.clear();
+ IncludeLocations.clear();
+ return Fixes;
+}
+
+IncludeSorter::IncludeStyle
+IncludeSorter::parseIncludeStyle(const std::string &Value) {
+ return Value == "llvm" ? IS_LLVM : IS_Google;
+}
+
+StringRef IncludeSorter::toString(IncludeStyle Style) {
+ return Style == IS_LLVM ? "llvm" : "google";
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===------------ IncludeSorter.h - clang-tidy ----------------------------===//
+//
+// 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_INCLUDESORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
+
+#include "../ClangTidy.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Class used by ``IncludeInserterCallback`` to record the names of the
+/// inclusions in a given source file being processed and generate the necessary
+/// commands to sort the inclusions according to the precedence encoded in
+/// ``IncludeKinds``.
+class IncludeSorter {
+public:
+ /// Supported include styles.
+ enum IncludeStyle { IS_LLVM = 0, IS_Google = 1 };
+
+ /// Converts "llvm" to ``IS_LLVM``, otherwise returns ``IS_Google``.
+ static IncludeStyle parseIncludeStyle(const std::string &Value);
+
+ /// Converts ``IncludeStyle`` to string representation.
+ static StringRef toString(IncludeStyle Style);
+
+ /// The classifications of inclusions, in the order they should be sorted.
+ enum IncludeKinds {
+ IK_MainTUInclude = 0, ///< e.g. ``#include "foo.h"`` when editing foo.cc
+ IK_CSystemInclude = 1, ///< e.g. ``#include <stdio.h>``
+ IK_CXXSystemInclude = 2, ///< e.g. ``#include <vector>``
+ IK_NonSystemInclude = 3, ///< e.g. ``#include "bar.h"``
+ IK_InvalidInclude = 4 ///< total number of valid ``IncludeKind``s
+ };
+
+ /// ``IncludeSorter`` constructor; takes the FileID and name of the file to be
+ /// processed by the sorter.
+ IncludeSorter(const SourceManager *SourceMgr, const LangOptions *LangOpts,
+ const FileID FileID, StringRef FileName, IncludeStyle Style);
+
+ /// Returns the ``SourceManager``-specific file ID for the file being handled
+ /// by the sorter.
+ const FileID current_FileID() const { return CurrentFileID; }
+
+ /// Adds the given include directive to the sorter.
+ void AddInclude(StringRef FileName, bool IsAngled,
+ SourceLocation HashLocation, SourceLocation EndLocation);
+
+ /// Returns the edits needed to sort the current set of includes and reset the
+ /// internal state (so that different blocks of includes are sorted separately
+ /// within the same file).
+ std::vector<FixItHint> GetEdits();
+
+ /// Creates a quoted inclusion directive in the right sort order. Returns None
+ /// on error or if header inclusion directive for header already exists.
+ Optional<FixItHint> CreateIncludeInsertion(StringRef FileName, bool IsAngled);
+
+private:
+ typedef SmallVector<SourceRange, 1> SourceRangeVector;
+
+ const SourceManager *SourceMgr;
+ const LangOptions *LangOpts;
+ const IncludeStyle Style;
+ FileID CurrentFileID;
+ /// The file name stripped of common suffixes.
+ StringRef CanonicalFile;
+ /// Locations of visited include directives.
+ SourceRangeVector SourceLocations;
+ /// Mapping from file name to #include locations.
+ llvm::StringMap<SourceRangeVector> IncludeLocations;
+ /// Includes sorted into buckets.
+ SmallVector<std::string, 1> IncludeBucket[IK_InvalidInclude];
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
--- /dev/null
+//===--- LexerUtils.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 "LexerUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace lexer {
+
+Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
+ bool SkipComments) {
+ const auto &SourceManager = Context.getSourceManager();
+ Token Token;
+ Token.setKind(tok::unknown);
+ Location = Location.getLocWithOffset(-1);
+ auto StartOfFile =
+ SourceManager.getLocForStartOfFile(SourceManager.getFileID(Location));
+ while (Location != StartOfFile) {
+ Location = Lexer::GetBeginningOfToken(Location, SourceManager,
+ Context.getLangOpts());
+ if (!Lexer::getRawToken(Location, Token, SourceManager,
+ Context.getLangOpts()) &&
+ (!SkipComments || !Token.is(tok::comment))) {
+ break;
+ }
+ Location = Location.getLocWithOffset(-1);
+ }
+ return Token;
+}
+
+} // namespace lexer
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- LexerUtils.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_UTILS_LEXER_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace lexer {
+
+/// Returns previous token or ``tok::unknown`` if not found.
+Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
+ bool SkipComments = true);
+
+} // namespace lexer
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
--- /dev/null
+//===--- Matchers.h - clang-tidy-------------------------------------------===//
+//
+// 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_UTILS_MATCHERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
+
+#include "TypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+namespace clang {
+namespace tidy {
+namespace matchers {
+
+AST_MATCHER(BinaryOperator, isRelationalOperator) {
+ return Node.isRelationalOp();
+}
+
+AST_MATCHER(BinaryOperator, isEqualityOperator) { return Node.isEqualityOp(); }
+
+AST_MATCHER(BinaryOperator, isComparisonOperator) {
+ return Node.isComparisonOp();
+}
+
+AST_MATCHER(QualType, isExpensiveToCopy) {
+ llvm::Optional<bool> IsExpensive =
+ utils::type_traits::isExpensiveToCopy(Node, Finder->getASTContext());
+ return IsExpensive && *IsExpensive;
+}
+
+AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) {
+ return utils::type_traits::recordIsTriviallyDefaultConstructible(
+ Node, Finder->getASTContext());
+}
+
+// Returns QualType matcher for references to const.
+AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isReferenceToConst) {
+ using namespace ast_matchers;
+ return referenceType(pointee(qualType(isConstQualified())));
+}
+
+} // namespace matchers
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
--- /dev/null
+//===---------- NamespaceAliaser.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 "NamespaceAliaser.h"
+
+#include "ASTUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+namespace clang {
+namespace tidy {
+namespace utils {
+
+using namespace ast_matchers;
+
+NamespaceAliaser::NamespaceAliaser(const SourceManager &SourceMgr)
+ : SourceMgr(SourceMgr) {}
+
+AST_MATCHER_P(NamespaceAliasDecl, hasTargetNamespace,
+ ast_matchers::internal::Matcher<NamespaceDecl>, innerMatcher) {
+ return innerMatcher.matches(*Node.getNamespace(), Finder, Builder);
+}
+
+Optional<FixItHint>
+NamespaceAliaser::createAlias(ASTContext &Context, const Stmt &Statement,
+ StringRef Namespace,
+ const std::vector<std::string> &Abbreviations) {
+ const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+ if (!Function || !Function->hasBody())
+ return None;
+
+ if (AddedAliases[Function].count(Namespace.str()) != 0)
+ return None;
+
+ // FIXME: Doesn't consider the order of declarations.
+ // If we accidentially pick an alias defined later in the function,
+ // the output won't compile.
+ // FIXME: Also doesn't consider file or class-scope aliases.
+
+ const auto *ExistingAlias = selectFirst<NamedDecl>(
+ "alias",
+ match(functionDecl(hasBody(compoundStmt(has(declStmt(
+ has(namespaceAliasDecl(hasTargetNamespace(hasName(Namespace)))
+ .bind("alias"))))))),
+ *Function, Context));
+
+ if (ExistingAlias != nullptr) {
+ AddedAliases[Function][Namespace.str()] = ExistingAlias->getName().str();
+ return None;
+ }
+
+ for (const auto &Abbreviation : Abbreviations) {
+ DeclarationMatcher ConflictMatcher = namedDecl(hasName(Abbreviation));
+ const auto HasConflictingChildren =
+ !match(findAll(ConflictMatcher), *Function, Context).empty();
+ const auto HasConflictingAncestors =
+ !match(functionDecl(hasAncestor(decl(has(ConflictMatcher)))), *Function,
+ Context)
+ .empty();
+ if (HasConflictingAncestors || HasConflictingChildren)
+ continue;
+
+ std::string Declaration =
+ (llvm::Twine("\nnamespace ") + Abbreviation + " = " + Namespace + ";")
+ .str();
+ SourceLocation Loc =
+ Lexer::getLocForEndOfToken(Function->getBody()->getLocStart(), 0,
+ SourceMgr, Context.getLangOpts());
+ AddedAliases[Function][Namespace.str()] = Abbreviation;
+ return FixItHint::CreateInsertion(Loc, Declaration);
+ }
+
+ return None;
+}
+
+std::string NamespaceAliaser::getNamespaceName(ASTContext &Context,
+ const Stmt &Statement,
+ StringRef Namespace) const {
+ const auto *Function = getSurroundingFunction(Context, Statement);
+ auto FunctionAliases = AddedAliases.find(Function);
+ if (FunctionAliases != AddedAliases.end()) {
+ if (FunctionAliases->second.count(Namespace) != 0) {
+ return FunctionAliases->second.find(Namespace)->getValue();
+ }
+ }
+ return Namespace.str();
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---------- NamespaceAliaser.h - clang-tidy ---------------------------===//
+//
+// 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_NAMESPACEALIASER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// This class creates function-level namespace aliases.
+class NamespaceAliaser {
+public:
+ explicit NamespaceAliaser(const SourceManager &SourceMgr);
+ // Adds a namespace alias for \p Namespace valid near \p
+ // Statement. Picks the first available name from \p Abbreviations.
+ // Returns ``llvm::None`` if an alias already exists or there is an error.
+ llvm::Optional<FixItHint>
+ createAlias(ASTContext &Context, const Stmt &Statement,
+ llvm::StringRef Namespace,
+ const std::vector<std::string> &Abbreviations);
+
+ // Get an alias name for \p Namespace valid at \p Statement. Returns \p
+ // Namespace if there is no alias.
+ std::string getNamespaceName(ASTContext &Context, const Stmt &Statement,
+ llvm::StringRef Namespace) const;
+
+private:
+ const SourceManager &SourceMgr;
+ llvm::DenseMap<const FunctionDecl *, llvm::StringMap<std::string>>
+ AddedAliases;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H
--- /dev/null
+//===--- DanglingHandleCheck.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 "OptionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace options {
+
+static const char StringsDelimiter[] = ";";
+
+std::vector<std::string> parseStringList(StringRef Option) {
+ SmallVector<StringRef, 4> Names;
+ Option.split(Names, StringsDelimiter);
+ std::vector<std::string> Result;
+ for (StringRef &Name : Names) {
+ Name = Name.trim();
+ if (!Name.empty())
+ Result.push_back(Name);
+ }
+ return Result;
+}
+
+std::string serializeStringList(ArrayRef<std::string> Strings) {
+ return llvm::join(Strings.begin(), Strings.end(), StringsDelimiter);
+}
+
+} // namespace options
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- DanglingHandleCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace options {
+
+/// \brief Parse a semicolon separated list of strings.
+std::vector<std::string> parseStringList(StringRef Option);
+
+/// \brief Serialize a sequence of names that can be parsed by
+/// ``parseStringList``.
+std::string serializeStringList(ArrayRef<std::string> Strings);
+
+} // namespace options
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
--- /dev/null
+//===--- TypeTraits.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 "TypeTraits.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace type_traits {
+
+namespace {
+
+bool classHasTrivialCopyAndDestroy(QualType Type) {
+ auto *Record = Type->getAsCXXRecordDecl();
+ return Record && Record->hasDefinition() &&
+ !Record->hasNonTrivialCopyConstructor() &&
+ !Record->hasNonTrivialDestructor();
+}
+
+bool hasDeletedCopyConstructor(QualType Type) {
+ auto *Record = Type->getAsCXXRecordDecl();
+ if (!Record || !Record->hasDefinition())
+ return false;
+ for (const auto *Constructor : Record->ctors()) {
+ if (Constructor->isCopyConstructor() && Constructor->isDeleted())
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+llvm::Optional<bool> isExpensiveToCopy(QualType Type,
+ const ASTContext &Context) {
+ if (Type->isDependentType() || Type->isIncompleteType())
+ return llvm::None;
+ return !Type.isTriviallyCopyableType(Context) &&
+ !classHasTrivialCopyAndDestroy(Type) &&
+ !hasDeletedCopyConstructor(Type);
+}
+
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+ const ASTContext &Context) {
+ const auto *ClassDecl = dyn_cast<CXXRecordDecl>(&RecordDecl);
+ // Non-C++ records are always trivially constructible.
+ if (!ClassDecl)
+ return true;
+ // A class with a user-provided default constructor is not trivially
+ // constructible.
+ if (ClassDecl->hasUserProvidedDefaultConstructor())
+ return false;
+ // A polymorphic class is not trivially constructible
+ if (ClassDecl->isPolymorphic())
+ return false;
+ // A class is trivially constructible if it has a trivial default constructor.
+ if (ClassDecl->hasTrivialDefaultConstructor())
+ return true;
+
+ // If all its fields are trivially constructible and have no default
+ // initializers.
+ for (const FieldDecl *Field : ClassDecl->fields()) {
+ if (Field->hasInClassInitializer())
+ return false;
+ if (!isTriviallyDefaultConstructible(Field->getType(), Context))
+ return false;
+ }
+ // If all its direct bases are trivially constructible.
+ for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
+ if (!isTriviallyDefaultConstructible(Base.getType(), Context))
+ return false;
+ if (Base.isVirtual())
+ return false;
+ }
+
+ return true;
+}
+
+// Based on QualType::isTrivial.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) {
+ if (Type.isNull())
+ return false;
+
+ if (Type->isArrayType())
+ return isTriviallyDefaultConstructible(Context.getBaseElementType(Type),
+ Context);
+
+ // Return false for incomplete types after skipping any incomplete array
+ // types which are expressly allowed by the standard and thus our API.
+ if (Type->isIncompleteType())
+ return false;
+
+ if (Context.getLangOpts().ObjCAutoRefCount) {
+ switch (Type.getObjCLifetime()) {
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+
+ case Qualifiers::OCL_None:
+ if (Type->isObjCLifetimeType())
+ return false;
+ break;
+ }
+ }
+
+ QualType CanonicalType = Type.getCanonicalType();
+ if (CanonicalType->isDependentType())
+ return false;
+
+ // As an extension, Clang treats vector types as Scalar types.
+ if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
+ return true;
+
+ if (const auto *RT = CanonicalType->getAs<RecordType>()) {
+ return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context);
+ }
+
+ // No other types can match.
+ return false;
+}
+
+bool hasNonTrivialMoveConstructor(QualType Type) {
+ auto *Record = Type->getAsCXXRecordDecl();
+ return Record && Record->hasDefinition() &&
+ Record->hasNonTrivialMoveConstructor();
+}
+
+bool hasNonTrivialMoveAssignment(QualType Type) {
+ auto *Record = Type->getAsCXXRecordDecl();
+ return Record && Record->hasDefinition() &&
+ Record->hasNonTrivialMoveAssignment();
+}
+
+} // namespace type_traits
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- TypeTraits.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_UTILS_TYPETRAITS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_TYPETRAITS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace type_traits {
+
+/// Returns `true` if `Type` is expensive to copy.
+llvm::Optional<bool> isExpensiveToCopy(QualType Type,
+ const ASTContext &Context);
+
+/// Returns `true` if `Type` is trivially default constructible.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context);
+
+/// Returns `true` if `RecordDecl` is trivially default constructible.
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+ const ASTContext &Context);
+
+/// Returns true if `Type` has a non-trivial move constructor.
+bool hasNonTrivialMoveConstructor(QualType Type);
+
+/// Return true if `Type` has a non-trivial move assignment operator.
+bool hasNonTrivialMoveAssignment(QualType Type);
+
+} // type_traits
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_TYPETRAITS_H
--- /dev/null
+//===---------- UsingInserter.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 "UsingInserter.h"
+
+#include "ASTUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+using namespace ast_matchers;
+
+static StringRef getUnqualifiedName(StringRef QualifiedName) {
+ size_t LastSeparatorPos = QualifiedName.rfind("::");
+ if (LastSeparatorPos == StringRef::npos)
+ return QualifiedName;
+ return QualifiedName.drop_front(LastSeparatorPos + 2);
+}
+
+UsingInserter::UsingInserter(const SourceManager &SourceMgr)
+ : SourceMgr(SourceMgr) {}
+
+Optional<FixItHint> UsingInserter::createUsingDeclaration(
+ ASTContext &Context, const Stmt &Statement, StringRef QualifiedName) {
+ StringRef UnqualifiedName = getUnqualifiedName(QualifiedName);
+ const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+ if (!Function)
+ return None;
+
+ if (AddedUsing.count(std::make_pair(Function, QualifiedName.str())) != 0)
+ return None;
+
+ SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
+ Function->getBody()->getLocStart(), 0, SourceMgr, Context.getLangOpts());
+
+ // Only use using declarations in the main file, not in includes.
+ if (SourceMgr.getFileID(InsertLoc) != SourceMgr.getMainFileID())
+ return None;
+
+ // FIXME: This declaration could be masked. Investigate if
+ // there is a way to avoid using Sema.
+ bool AlreadyHasUsingDecl =
+ !match(stmt(hasAncestor(decl(has(usingDecl(hasAnyUsingShadowDecl(
+ hasTargetDecl(hasName(QualifiedName.str())))))))),
+ Statement, Context)
+ .empty();
+ if (AlreadyHasUsingDecl) {
+ AddedUsing.emplace(NameInFunction(Function, QualifiedName.str()));
+ return None;
+ }
+ // Find conflicting declarations and references.
+ auto ConflictingDecl = namedDecl(hasName(UnqualifiedName));
+ bool HasConflictingDeclaration =
+ !match(findAll(ConflictingDecl), *Function, Context).empty();
+ bool HasConflictingDeclRef =
+ !match(findAll(declRefExpr(to(ConflictingDecl))), *Function, Context)
+ .empty();
+ if (HasConflictingDeclaration || HasConflictingDeclRef)
+ return None;
+
+ std::string Declaration =
+ (llvm::Twine("\nusing ") + QualifiedName + ";").str();
+
+ AddedUsing.emplace(std::make_pair(Function, QualifiedName.str()));
+ return FixItHint::CreateInsertion(InsertLoc, Declaration);
+}
+
+StringRef UsingInserter::getShortName(ASTContext &Context,
+ const Stmt &Statement,
+ StringRef QualifiedName) {
+ const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+ if (AddedUsing.count(NameInFunction(Function, QualifiedName.str())) != 0)
+ return getUnqualifiedName(QualifiedName);
+ return QualifiedName;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---------- UsingInserter.h - clang-tidy ----------------------------===//
+//
+// 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_USINGINSERTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include <set>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// UsingInserter adds using declarations for |QualifiedName| to the surrounding
+// function.
+// This allows using a shorter name without clobbering other scopes.
+class UsingInserter {
+public:
+ UsingInserter(const SourceManager &SourceMgr);
+
+ // Creates a \p using declaration fixit. Returns ``llvm::None`` on error
+ // or if the using declaration already exists.
+ llvm::Optional<FixItHint>
+ createUsingDeclaration(ASTContext &Context, const Stmt &Statement,
+ llvm::StringRef QualifiedName);
+
+ // Returns the unqualified version of the name if there is an
+ // appropriate using declaration and the qualified name otherwise.
+ llvm::StringRef getShortName(ASTContext &Context, const Stmt &Statement,
+ llvm::StringRef QualifiedName);
+
+private:
+ typedef std::pair<const FunctionDecl *, std::string> NameInFunction;
+ const SourceManager &SourceMgr;
+ std::set<NameInFunction> AddedUsing;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(clangDaemon
+ ClangdLSPServer.cpp
+ ClangdServer.cpp
+ ClangdUnit.cpp
+ ClangdUnitStore.cpp
+ DraftStore.cpp
+ GlobalCompilationDatabase.cpp
+ JSONRPCDispatcher.cpp
+ Protocol.cpp
+ ProtocolHandlers.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangIndex
+ clangLex
+ clangSema
+ clangTooling
+ clangToolingCore
+ ${LLVM_PTHREAD_LIB}
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ClangdLSPServer.h"
+#include "JSONRPCDispatcher.h"
+#include "ProtocolHandlers.h"
+
+using namespace clang::clangd;
+using namespace clang;
+
+namespace {
+
+std::string
+replacementsToEdits(StringRef Code,
+ const std::vector<tooling::Replacement> &Replacements) {
+ // Turn the replacements into the format specified by the Language Server
+ // Protocol. Fuse them into one big JSON array.
+ std::string Edits;
+ for (auto &R : Replacements) {
+ Range ReplacementRange = {
+ offsetToPosition(Code, R.getOffset()),
+ offsetToPosition(Code, R.getOffset() + R.getLength())};
+ TextEdit TE = {ReplacementRange, R.getReplacementText()};
+ Edits += TextEdit::unparse(TE);
+ Edits += ',';
+ }
+ if (!Edits.empty())
+ Edits.pop_back();
+
+ return Edits;
+}
+
+} // namespace
+
+ClangdLSPServer::LSPDiagnosticsConsumer::LSPDiagnosticsConsumer(
+ ClangdLSPServer &Server)
+ : Server(Server) {}
+
+void ClangdLSPServer::LSPDiagnosticsConsumer::onDiagnosticsReady(
+ PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
+ Server.consumeDiagnostics(File, Diagnostics.Value);
+}
+
+class ClangdLSPServer::LSPProtocolCallbacks : public ProtocolCallbacks {
+public:
+ LSPProtocolCallbacks(ClangdLSPServer &LangServer) : LangServer(LangServer) {}
+
+ void onInitialize(StringRef ID, JSONOutput &Out) override;
+ void onShutdown(JSONOutput &Out) override;
+ void onDocumentDidOpen(DidOpenTextDocumentParams Params,
+ JSONOutput &Out) override;
+ void onDocumentDidChange(DidChangeTextDocumentParams Params,
+ JSONOutput &Out) override;
+ void onDocumentDidClose(DidCloseTextDocumentParams Params,
+ JSONOutput &Out) override;
+ void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
+ StringRef ID, JSONOutput &Out) override;
+ void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
+ StringRef ID, JSONOutput &Out) override;
+ void onDocumentFormatting(DocumentFormattingParams Params, StringRef ID,
+ JSONOutput &Out) override;
+ void onCodeAction(CodeActionParams Params, StringRef ID,
+ JSONOutput &Out) override;
+ void onCompletion(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) override;
+ void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) override;
+
+private:
+ ClangdLSPServer &LangServer;
+};
+
+void ClangdLSPServer::LSPProtocolCallbacks::onInitialize(StringRef ID,
+ JSONOutput &Out) {
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID +
+ R"(,"result":{"capabilities":{
+ "textDocumentSync": 1,
+ "documentFormattingProvider": true,
+ "documentRangeFormattingProvider": true,
+ "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+ "codeActionProvider": true,
+ "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+ "definitionProvider": true
+ }}})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onShutdown(JSONOutput &Out) {
+ LangServer.IsDone = true;
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidOpen(
+ DidOpenTextDocumentParams Params, JSONOutput &Out) {
+ if (Params.metadata && !Params.metadata->extraFlags.empty())
+ LangServer.CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
+ std::move(Params.metadata->extraFlags));
+ LangServer.Server.addDocument(Params.textDocument.uri.file,
+ Params.textDocument.text);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidChange(
+ DidChangeTextDocumentParams Params, JSONOutput &Out) {
+ // We only support full syncing right now.
+ LangServer.Server.addDocument(Params.textDocument.uri.file,
+ Params.contentChanges[0].text);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidClose(
+ DidCloseTextDocumentParams Params, JSONOutput &Out) {
+ LangServer.Server.removeDocument(Params.textDocument.uri.file);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentOnTypeFormatting(
+ DocumentOnTypeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+ auto File = Params.textDocument.uri.file;
+ std::string Code = LangServer.Server.getDocument(File);
+ std::string Edits = replacementsToEdits(
+ Code, LangServer.Server.formatOnType(File, Params.position));
+
+ Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentRangeFormatting(
+ DocumentRangeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+ auto File = Params.textDocument.uri.file;
+ std::string Code = LangServer.Server.getDocument(File);
+ std::string Edits = replacementsToEdits(
+ Code, LangServer.Server.formatRange(File, Params.range));
+
+ Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentFormatting(
+ DocumentFormattingParams Params, StringRef ID, JSONOutput &Out) {
+ auto File = Params.textDocument.uri.file;
+ std::string Code = LangServer.Server.getDocument(File);
+ std::string Edits =
+ replacementsToEdits(Code, LangServer.Server.formatFile(File));
+
+ Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onCodeAction(
+ CodeActionParams Params, StringRef ID, JSONOutput &Out) {
+ // We provide a code action for each diagnostic at the requested location
+ // which has FixIts available.
+ std::string Code =
+ LangServer.Server.getDocument(Params.textDocument.uri.file);
+ std::string Commands;
+ for (Diagnostic &D : Params.context.diagnostics) {
+ std::vector<clang::tooling::Replacement> Fixes =
+ LangServer.getFixIts(Params.textDocument.uri.file, D);
+ std::string Edits = replacementsToEdits(Code, Fixes);
+
+ if (!Edits.empty())
+ Commands +=
+ R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
+ R"('", "command": "clangd.applyFix", "arguments": [")" +
+ llvm::yaml::escape(Params.textDocument.uri.uri) +
+ R"(", [)" + Edits +
+ R"(]]},)";
+ }
+ if (!Commands.empty())
+ Commands.pop_back();
+
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(, "result": [)" + Commands +
+ R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onCompletion(
+ TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+ auto Items = LangServer.Server.codeComplete(
+ Params.textDocument.uri.file,
+ Position{Params.position.line, Params.position.character}).Value;
+
+ std::string Completions;
+ for (const auto &Item : Items) {
+ Completions += CompletionItem::unparse(Item);
+ Completions += ",";
+ }
+ if (!Completions.empty())
+ Completions.pop_back();
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Completions + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition(
+ TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+ auto Items = LangServer.Server.findDefinitions(
+ Params.textDocument.uri.file,
+ Position{Params.position.line, Params.position.character}).Value;
+
+ std::string Locations;
+ for (const auto &Item : Items) {
+ Locations += Location::unparse(Item);
+ Locations += ",";
+ }
+ if (!Locations.empty())
+ Locations.pop_back();
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID.str() +
+ R"(,"result":[)" + Locations + R"(]})");
+}
+
+ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
+ : Out(Out), DiagConsumer(*this),
+ Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {}
+
+void ClangdLSPServer::run(std::istream &In) {
+ assert(!IsDone && "Run was called before");
+
+ // Set up JSONRPCDispatcher.
+ LSPProtocolCallbacks Callbacks(*this);
+ JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
+ regiterCallbackHandlers(Dispatcher, Out, Callbacks);
+
+ // Run the Language Server loop.
+ runLanguageServerLoop(In, Out, Dispatcher, IsDone);
+
+ // Make sure IsDone is set to true after this method exits to ensure assertion
+ // at the start of the method fires if it's ever executed again.
+ IsDone = true;
+}
+
+std::vector<clang::tooling::Replacement>
+ClangdLSPServer::getFixIts(StringRef File, const clangd::Diagnostic &D) {
+ std::lock_guard<std::mutex> Lock(FixItsMutex);
+ auto DiagToFixItsIter = FixItsMap.find(File);
+ if (DiagToFixItsIter == FixItsMap.end())
+ return {};
+
+ const auto &DiagToFixItsMap = DiagToFixItsIter->second;
+ auto FixItsIter = DiagToFixItsMap.find(D);
+ if (FixItsIter == DiagToFixItsMap.end())
+ return {};
+
+ return FixItsIter->second;
+}
+
+void ClangdLSPServer::consumeDiagnostics(
+ PathRef File, std::vector<DiagWithFixIts> Diagnostics) {
+ std::string DiagnosticsJSON;
+
+ DiagnosticToReplacementMap LocalFixIts; // Temporary storage
+ for (auto &DiagWithFixes : Diagnostics) {
+ auto Diag = DiagWithFixes.Diag;
+ DiagnosticsJSON +=
+ R"({"range":)" + Range::unparse(Diag.range) +
+ R"(,"severity":)" + std::to_string(Diag.severity) +
+ R"(,"message":")" + llvm::yaml::escape(Diag.message) +
+ R"("},)";
+
+ // We convert to Replacements to become independent of the SourceManager.
+ auto &FixItsForDiagnostic = LocalFixIts[Diag];
+ std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
+ std::back_inserter(FixItsForDiagnostic));
+ }
+
+ // Cache FixIts
+ {
+ // FIXME(ibiryukov): should be deleted when documents are removed
+ std::lock_guard<std::mutex> Lock(FixItsMutex);
+ FixItsMap[File] = LocalFixIts;
+ }
+
+ // Publish diagnostics.
+ if (!DiagnosticsJSON.empty())
+ DiagnosticsJSON.pop_back(); // Drop trailing comma.
+ Out.writeMessage(
+ R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
+ URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +
+ R"(]}})");
+}
--- /dev/null
+//===--- ClangdLSPServer.h - LSP server --------------------------*- 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_CLANGD_CLANGDLSPSERVER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
+
+#include "ClangdServer.h"
+#include "GlobalCompilationDatabase.h"
+#include "Path.h"
+#include "Protocol.h"
+#include "clang/Tooling/Core/Replacement.h"
+
+namespace clang {
+namespace clangd {
+
+class JSONOutput;
+
+/// This class provides implementation of an LSP server, glueing the JSON
+/// dispatch and ClangdServer together.
+class ClangdLSPServer {
+public:
+ ClangdLSPServer(JSONOutput &Out, bool RunSynchronously);
+
+ /// Run LSP server loop, receiving input for it from \p In. \p In must be
+ /// opened in binary mode. Output will be written using Out variable passed to
+ /// class constructor. This method must not be executed more than once for
+ /// each instance of ClangdLSPServer.
+ void run(std::istream &In);
+
+private:
+ class LSPProtocolCallbacks;
+ class LSPDiagnosticsConsumer : public DiagnosticsConsumer {
+ public:
+ LSPDiagnosticsConsumer(ClangdLSPServer &Server);
+
+ virtual void
+ onDiagnosticsReady(PathRef File,
+ Tagged<std::vector<DiagWithFixIts>> Diagnostics);
+
+ private:
+ ClangdLSPServer &Server;
+ };
+
+ std::vector<clang::tooling::Replacement>
+ getFixIts(StringRef File, const clangd::Diagnostic &D);
+
+ /// Function that will be called on a separate thread when diagnostics are
+ /// ready. Sends the Dianostics to LSP client via Out.writeMessage and caches
+ /// corresponding fixits in the FixItsMap.
+ void consumeDiagnostics(PathRef File,
+ std::vector<DiagWithFixIts> Diagnostics);
+
+ JSONOutput &Out;
+ /// Used to indicate that the 'shutdown' request was received from the
+ /// Language Server client.
+ /// It's used to break out of the LSP parsing loop.
+ bool IsDone = false;
+
+ std::mutex FixItsMutex;
+ typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
+ DiagnosticToReplacementMap;
+ /// Caches FixIts per file and diagnostics
+ llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
+
+ // Various ClangdServer parameters go here. It's important they're created
+ // before ClangdServer.
+ DirectoryBasedGlobalCompilationDatabase CDB;
+ LSPDiagnosticsConsumer DiagConsumer;
+ RealFileSystemProvider FSProvider;
+
+ // Server must be the last member of the class to allow its destructor to exit
+ // the worker thread that may otherwise run an async callback on partially
+ // destructed instance of ClangdLSPServer.
+ ClangdServer Server;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <future>
+
+using namespace clang;
+using namespace clang::clangd;
+
+namespace {
+
+std::vector<tooling::Replacement> formatCode(StringRef Code, StringRef Filename,
+ ArrayRef<tooling::Range> Ranges) {
+ // Call clang-format.
+ // FIXME: Don't ignore style.
+ format::FormatStyle Style = format::getLLVMStyle();
+ auto Result = format::reformat(Style, Code, Ranges, Filename);
+
+ return std::vector<tooling::Replacement>(Result.begin(), Result.end());
+}
+
+std::string getStandardResourceDir() {
+ static int Dummy; // Just an address in this process.
+ return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
+}
+
+} // namespace
+
+size_t clangd::positionToOffset(StringRef Code, Position P) {
+ size_t Offset = 0;
+ for (int I = 0; I != P.line; ++I) {
+ // FIXME: \r\n
+ // FIXME: UTF-8
+ size_t F = Code.find('\n', Offset);
+ if (F == StringRef::npos)
+ return 0; // FIXME: Is this reasonable?
+ Offset = F + 1;
+ }
+ return (Offset == 0 ? 0 : (Offset - 1)) + P.character;
+}
+
+/// Turn an offset in Code into a [line, column] pair.
+Position clangd::offsetToPosition(StringRef Code, size_t Offset) {
+ StringRef JustBefore = Code.substr(0, Offset);
+ // FIXME: \r\n
+ // FIXME: UTF-8
+ int Lines = JustBefore.count('\n');
+ int Cols = JustBefore.size() - JustBefore.rfind('\n') - 1;
+ return {Lines, Cols};
+}
+
+Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+RealFileSystemProvider::getTaggedFileSystem(PathRef File) {
+ return make_tagged(vfs::getRealFileSystem(), VFSTag());
+}
+
+ClangdScheduler::ClangdScheduler(bool RunSynchronously)
+ : RunSynchronously(RunSynchronously) {
+ if (RunSynchronously) {
+ // Don't start the worker thread if we're running synchronously
+ return;
+ }
+
+ // Initialize Worker in ctor body, rather than init list to avoid potentially
+ // using not-yet-initialized members
+ Worker = std::thread([this]() {
+ while (true) {
+ std::function<void()> Request;
+
+ // Pick request from the queue
+ {
+ std::unique_lock<std::mutex> Lock(Mutex);
+ // Wait for more requests.
+ RequestCV.wait(Lock, [this] { return !RequestQueue.empty() || Done; });
+ if (Done)
+ return;
+
+ assert(!RequestQueue.empty() && "RequestQueue was empty");
+
+ // We process requests starting from the front of the queue. Users of
+ // ClangdScheduler have a way to prioritise their requests by putting
+ // them to the either side of the queue (using either addToEnd or
+ // addToFront).
+ Request = std::move(RequestQueue.front());
+ RequestQueue.pop_front();
+ } // unlock Mutex
+
+ Request();
+ }
+ });
+}
+
+ClangdScheduler::~ClangdScheduler() {
+ if (RunSynchronously)
+ return; // no worker thread is running in that case
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ // Wake up the worker thread
+ Done = true;
+ } // unlock Mutex
+ RequestCV.notify_one();
+ Worker.join();
+}
+
+void ClangdScheduler::addToFront(std::function<void()> Request) {
+ if (RunSynchronously) {
+ Request();
+ return;
+ }
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ RequestQueue.push_front(Request);
+ }
+ RequestCV.notify_one();
+}
+
+void ClangdScheduler::addToEnd(std::function<void()> Request) {
+ if (RunSynchronously) {
+ Request();
+ return;
+ }
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ RequestQueue.push_back(Request);
+ }
+ RequestCV.notify_one();
+}
+
+ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB,
+ DiagnosticsConsumer &DiagConsumer,
+ FileSystemProvider &FSProvider,
+ bool RunSynchronously,
+ llvm::Optional<StringRef> ResourceDir)
+ : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider),
+ ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()),
+ PCHs(std::make_shared<PCHContainerOperations>()),
+ WorkScheduler(RunSynchronously) {}
+
+void ClangdServer::addDocument(PathRef File, StringRef Contents) {
+ DocVersion Version = DraftMgr.updateDraft(File, Contents);
+ Path FileStr = File;
+ WorkScheduler.addToFront([this, FileStr, Version]() {
+ auto FileContents = DraftMgr.getDraft(FileStr);
+ if (FileContents.Version != Version)
+ return; // This request is outdated, do nothing
+
+ assert(FileContents.Draft &&
+ "No contents inside a file that was scheduled for reparse");
+ auto TaggedFS = FSProvider.getTaggedFileSystem(FileStr);
+ Units.runOnUnit(
+ FileStr, *FileContents.Draft, ResourceDir, CDB, PCHs, TaggedFS.Value,
+ [&](ClangdUnit const &Unit) {
+ DiagConsumer.onDiagnosticsReady(
+ FileStr, make_tagged(Unit.getLocalDiagnostics(), TaggedFS.Tag));
+ });
+ });
+}
+
+void ClangdServer::removeDocument(PathRef File) {
+ auto Version = DraftMgr.removeDraft(File);
+ Path FileStr = File;
+ WorkScheduler.addToFront([this, FileStr, Version]() {
+ if (Version != DraftMgr.getVersion(FileStr))
+ return; // This request is outdated, do nothing
+
+ Units.removeUnitIfPresent(FileStr);
+ });
+}
+
+void ClangdServer::forceReparse(PathRef File) {
+ // The addDocument schedules the reparse even if the contents of the file
+ // never changed, so we just call it here.
+ addDocument(File, getDocument(File));
+}
+
+Tagged<std::vector<CompletionItem>>
+ClangdServer::codeComplete(PathRef File, Position Pos,
+ llvm::Optional<StringRef> OverridenContents) {
+ std::string DraftStorage;
+ if (!OverridenContents) {
+ auto FileContents = DraftMgr.getDraft(File);
+ assert(FileContents.Draft &&
+ "codeComplete is called for non-added document");
+
+ DraftStorage = std::move(*FileContents.Draft);
+ OverridenContents = DraftStorage;
+ }
+
+ std::vector<CompletionItem> Result;
+ auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+ // It would be nice to use runOnUnitWithoutReparse here, but we can't
+ // guarantee the correctness of code completion cache here if we don't do the
+ // reparse.
+ Units.runOnUnit(File, *OverridenContents, ResourceDir, CDB, PCHs,
+ TaggedFS.Value, [&](ClangdUnit &Unit) {
+ Result = Unit.codeComplete(*OverridenContents, Pos,
+ TaggedFS.Value);
+ });
+ return make_tagged(std::move(Result), TaggedFS.Tag);
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatRange(PathRef File,
+ Range Rng) {
+ std::string Code = getDocument(File);
+
+ size_t Begin = positionToOffset(Code, Rng.start);
+ size_t Len = positionToOffset(Code, Rng.end) - Begin;
+ return formatCode(Code, File, {tooling::Range(Begin, Len)});
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatFile(PathRef File) {
+ // Format everything.
+ std::string Code = getDocument(File);
+ return formatCode(Code, File, {tooling::Range(0, Code.size())});
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatOnType(PathRef File,
+ Position Pos) {
+ // Look for the previous opening brace from the character position and
+ // format starting from there.
+ std::string Code = getDocument(File);
+ size_t CursorPos = positionToOffset(Code, Pos);
+ size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos);
+ if (PreviousLBracePos == StringRef::npos)
+ PreviousLBracePos = CursorPos;
+ size_t Len = 1 + CursorPos - PreviousLBracePos;
+
+ return formatCode(Code, File, {tooling::Range(PreviousLBracePos, Len)});
+}
+
+std::string ClangdServer::getDocument(PathRef File) {
+ auto draft = DraftMgr.getDraft(File);
+ assert(draft.Draft && "File is not tracked, cannot get contents");
+ return *draft.Draft;
+}
+
+std::string ClangdServer::dumpAST(PathRef File) {
+ std::promise<std::string> DumpPromise;
+ auto DumpFuture = DumpPromise.get_future();
+ auto Version = DraftMgr.getVersion(File);
+
+ WorkScheduler.addToEnd([this, &DumpPromise, File, Version]() {
+ assert(DraftMgr.getVersion(File) == Version && "Version has changed");
+ (void)Version;
+
+ Units.runOnExistingUnit(File, [&DumpPromise](ClangdUnit &Unit) {
+ std::string Result;
+
+ llvm::raw_string_ostream ResultOS(Result);
+ Unit.dumpAST(ResultOS);
+ ResultOS.flush();
+
+ DumpPromise.set_value(std::move(Result));
+ });
+ });
+ return DumpFuture.get();
+}
+
+Tagged<std::vector<Location>>
+ClangdServer::findDefinitions(PathRef File, Position Pos) {
+ auto FileContents = DraftMgr.getDraft(File);
+ assert(FileContents.Draft && "findDefinitions is called for non-added document");
+
+ std::vector<Location> Result;
+ auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+ Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs,
+ TaggedFS.Value, [&](ClangdUnit &Unit) {
+ Result = Unit.findDefinitions(Pos);
+ });
+ return make_tagged(std::move(Result), TaggedFS.Tag);
+}
--- /dev/null
+//===--- ClangdServer.h - Main clangd server code ----------------*- 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_CLANGD_CLANGDSERVER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
+
+#include "ClangdUnitStore.h"
+#include "DraftStore.h"
+#include "GlobalCompilationDatabase.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "ClangdUnit.h"
+#include "Protocol.h"
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <utility>
+
+namespace clang {
+class PCHContainerOperations;
+
+namespace clangd {
+
+/// Turn a [line, column] pair into an offset in Code.
+size_t positionToOffset(StringRef Code, Position P);
+
+/// Turn an offset in Code into a [line, column] pair.
+Position offsetToPosition(StringRef Code, size_t Offset);
+
+/// A tag supplied by the FileSytemProvider.
+typedef std::string VFSTag;
+
+/// A value of an arbitrary type and VFSTag that was supplied by the
+/// FileSystemProvider when this value was computed.
+template <class T> class Tagged {
+public:
+ template <class U>
+ Tagged(U &&Value, VFSTag Tag)
+ : Value(std::forward<U>(Value)), Tag(std::move(Tag)) {}
+
+ template <class U>
+ Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
+
+ template <class U>
+ Tagged(Tagged<U> &&Other)
+ : Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
+
+ T Value;
+ VFSTag Tag;
+};
+
+template <class T>
+Tagged<typename std::decay<T>::type> make_tagged(T &&Value, VFSTag Tag) {
+ return Tagged<T>(std::forward<T>(Value), Tag);
+}
+
+class DiagnosticsConsumer {
+public:
+ virtual ~DiagnosticsConsumer() = default;
+
+ /// Called by ClangdServer when \p Diagnostics for \p File are ready.
+ virtual void
+ onDiagnosticsReady(PathRef File,
+ Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
+};
+
+class FileSystemProvider {
+public:
+ virtual ~FileSystemProvider() = default;
+ /// Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
+ /// Name of the file that will be parsed is passed in \p File.
+ ///
+ /// \return A filesystem that will be used for all file accesses in clangd.
+ /// A Tag returned by this method will be propagated to all results of clangd
+ /// that will use this filesystem.
+ virtual Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+ getTaggedFileSystem(PathRef File) = 0;
+};
+
+class RealFileSystemProvider : public FileSystemProvider {
+public:
+ /// \return getRealFileSystem() tagged with default tag, i.e. VFSTag()
+ Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+ getTaggedFileSystem(PathRef File) override;
+};
+
+class ClangdServer;
+
+/// Handles running WorkerRequests of ClangdServer on a separate threads.
+/// Currently runs only one worker thread.
+class ClangdScheduler {
+public:
+ ClangdScheduler(bool RunSynchronously);
+ ~ClangdScheduler();
+
+ /// Add \p Request to the start of the queue. \p Request will be run on a
+ /// separate worker thread.
+ /// \p Request is scheduled to be executed before all currently added
+ /// requests.
+ void addToFront(std::function<void()> Request);
+ /// Add \p Request to the end of the queue. \p Request will be run on a
+ /// separate worker thread.
+ /// \p Request is scheduled to be executed after all currently added
+ /// requests.
+ void addToEnd(std::function<void()> Request);
+
+private:
+ bool RunSynchronously;
+ std::mutex Mutex;
+ /// We run some tasks on a separate thread(parsing, ClangdUnit cleanup).
+ /// This thread looks into RequestQueue to find requests to handle and
+ /// terminates when Done is set to true.
+ std::thread Worker;
+ /// Setting Done to true will make the worker thread terminate.
+ bool Done = false;
+ /// A queue of requests.
+ /// FIXME(krasimir): code completion should always have priority over parsing
+ /// for diagnostics.
+ std::deque<std::function<void()>> RequestQueue;
+ /// Condition variable to wake up the worker thread.
+ std::condition_variable RequestCV;
+};
+
+/// Provides API to manage ASTs for a collection of C++ files and request
+/// various language features(currently, only codeCompletion and async
+/// diagnostics for tracked files).
+class ClangdServer {
+public:
+ /// Creates a new ClangdServer. If \p RunSynchronously is false, no worker
+ /// thread will be created and all requests will be completed synchronously on
+ /// the calling thread (this is mostly used for tests). If \p RunSynchronously
+ /// is true, a worker thread will be created to parse files in the background
+ /// and provide diagnostics results via DiagConsumer.onDiagnosticsReady
+ /// callback. File accesses for each instance of parsing will be conducted via
+ /// a vfs::FileSystem provided by \p FSProvider. Results of code
+ /// completion/diagnostics also include a tag, that \p FSProvider returns
+ /// along with the vfs::FileSystem.
+ /// When \p ResourceDir is set, it will be used to search for internal headers
+ /// (overriding defaults and -resource-dir compiler flag, if set). If \p
+ /// ResourceDir is None, ClangdServer will attempt to set it to a standard
+ /// location, obtained via CompilerInvocation::GetResourcePath.
+ ClangdServer(GlobalCompilationDatabase &CDB,
+ DiagnosticsConsumer &DiagConsumer,
+ FileSystemProvider &FSProvider, bool RunSynchronously,
+ llvm::Optional<StringRef> ResourceDir = llvm::None);
+
+ /// Add a \p File to the list of tracked C++ files or update the contents if
+ /// \p File is already tracked. Also schedules parsing of the AST for it on a
+ /// separate thread. When the parsing is complete, DiagConsumer passed in
+ /// constructor will receive onDiagnosticsReady callback.
+ void addDocument(PathRef File, StringRef Contents);
+ /// Remove \p File from list of tracked files, schedule a request to free
+ /// resources associated with it.
+ void removeDocument(PathRef File);
+ /// Force \p File to be reparsed using the latest contents.
+ void forceReparse(PathRef File);
+
+ /// Run code completion for \p File at \p Pos. If \p OverridenContents is not
+ /// None, they will used only for code completion, i.e. no diagnostics update
+ /// will be scheduled and a draft for \p File will not be updated.
+ /// If \p OverridenContents is None, contents of the current draft for \p File
+ /// will be used.
+ /// This method should only be called for currently tracked files.
+ Tagged<std::vector<CompletionItem>>
+ codeComplete(PathRef File, Position Pos,
+ llvm::Optional<StringRef> OverridenContents = llvm::None);
+ /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+ Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
+
+ /// Run formatting for \p Rng inside \p File.
+ std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
+ /// Run formatting for the whole \p File.
+ std::vector<tooling::Replacement> formatFile(PathRef File);
+ /// Run formatting after a character was typed at \p Pos in \p File.
+ std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos);
+
+ /// Gets current document contents for \p File. \p File must point to a
+ /// currently tracked file.
+ /// FIXME(ibiryukov): This function is here to allow offset-to-Position
+ /// conversions in outside code, maybe there's a way to get rid of it.
+ std::string getDocument(PathRef File);
+
+ /// Only for testing purposes.
+ /// Waits until all requests to worker thread are finished and dumps AST for
+ /// \p File. \p File must be in the list of added documents.
+ std::string dumpAST(PathRef File);
+
+private:
+ GlobalCompilationDatabase &CDB;
+ DiagnosticsConsumer &DiagConsumer;
+ FileSystemProvider &FSProvider;
+ DraftStore DraftMgr;
+ ClangdUnitStore Units;
+ std::string ResourceDir;
+ std::shared_ptr<PCHContainerOperations> PCHs;
+ // WorkScheduler has to be the last member, because its destructor has to be
+ // called before all other members to stop the worker thread that references
+ // ClangdServer
+ ClangdScheduler WorkScheduler;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ClangdUnit.h"
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/Format.h"
+
+#include <algorithm>
+
+using namespace clang::clangd;
+using namespace clang;
+
+ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
+ StringRef ResourceDir,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ std::vector<tooling::CompileCommand> Commands,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS)
+ : FileName(FileName), PCHs(PCHs) {
+ assert(!Commands.empty() && "No compile commands provided");
+
+ // Inject the resource dir.
+ // FIXME: Don't overwrite it if it's already there.
+ Commands.front().CommandLine.push_back("-resource-dir=" +
+ std::string(ResourceDir));
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions);
+
+ std::vector<const char *> ArgStrs;
+ for (const auto &S : Commands.front().CommandLine)
+ ArgStrs.push_back(S.c_str());
+
+ ASTUnit::RemappedFile RemappedSource(
+ FileName,
+ llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+ auto ArgP = &*ArgStrs.begin();
+ Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
+ ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
+ /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
+ /*RemappedFilesKeepOriginalName=*/true,
+ /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
+ /*CacheCodeCompletionResults=*/true,
+ /*IncludeBriefCommentsInCodeCompletion=*/true,
+ /*AllowPCHWithCompilerErrors=*/true,
+ /*SkipFunctionBodies=*/false,
+ /*SingleFileParse=*/false,
+ /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
+ /*ModuleFormat=*/llvm::None,
+ /*ErrAST=*/nullptr, VFS));
+ assert(Unit && "Unit wasn't created");
+}
+
+void ClangdUnit::reparse(StringRef Contents,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+ // Do a reparse if this wasn't the first parse.
+ // FIXME: This might have the wrong working directory if it changed in the
+ // meantime.
+ ASTUnit::RemappedFile RemappedSource(
+ FileName,
+ llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+ Unit->Reparse(PCHs, RemappedSource, VFS);
+}
+
+namespace {
+
+CompletionItemKind getKind(CXCursorKind K) {
+ switch (K) {
+ case CXCursor_MacroInstantiation:
+ case CXCursor_MacroDefinition:
+ return CompletionItemKind::Text;
+ case CXCursor_CXXMethod:
+ return CompletionItemKind::Method;
+ case CXCursor_FunctionDecl:
+ case CXCursor_FunctionTemplate:
+ return CompletionItemKind::Function;
+ case CXCursor_Constructor:
+ case CXCursor_Destructor:
+ return CompletionItemKind::Constructor;
+ case CXCursor_FieldDecl:
+ return CompletionItemKind::Field;
+ case CXCursor_VarDecl:
+ case CXCursor_ParmDecl:
+ return CompletionItemKind::Variable;
+ case CXCursor_ClassDecl:
+ case CXCursor_StructDecl:
+ case CXCursor_UnionDecl:
+ case CXCursor_ClassTemplate:
+ case CXCursor_ClassTemplatePartialSpecialization:
+ return CompletionItemKind::Class;
+ case CXCursor_Namespace:
+ case CXCursor_NamespaceAlias:
+ case CXCursor_NamespaceRef:
+ return CompletionItemKind::Module;
+ case CXCursor_EnumConstantDecl:
+ return CompletionItemKind::Value;
+ case CXCursor_EnumDecl:
+ return CompletionItemKind::Enum;
+ case CXCursor_TypeAliasDecl:
+ case CXCursor_TypeAliasTemplateDecl:
+ case CXCursor_TypedefDecl:
+ case CXCursor_MemberRef:
+ case CXCursor_TypeRef:
+ return CompletionItemKind::Reference;
+ default:
+ return CompletionItemKind::Missing;
+ }
+}
+
+class CompletionItemsCollector : public CodeCompleteConsumer {
+ std::vector<CompletionItem> *Items;
+ std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
+ CodeCompletionTUInfo CCTUInfo;
+
+public:
+ CompletionItemsCollector(std::vector<CompletionItem> *Items,
+ const CodeCompleteOptions &CodeCompleteOpts)
+ : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
+ Items(Items),
+ Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
+ CCTUInfo(Allocator) {}
+
+ void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
+ CodeCompletionResult *Results,
+ unsigned NumResults) override {
+ for (unsigned I = 0; I != NumResults; ++I) {
+ CodeCompletionResult &Result = Results[I];
+ CodeCompletionString *CCS = Result.CreateCodeCompletionString(
+ S, Context, *Allocator, CCTUInfo,
+ CodeCompleteOpts.IncludeBriefComments);
+ if (CCS) {
+ CompletionItem Item;
+ for (CodeCompletionString::Chunk C : *CCS) {
+ switch (C.Kind) {
+ case CodeCompletionString::CK_ResultType:
+ Item.detail = C.Text;
+ break;
+ case CodeCompletionString::CK_Optional:
+ break;
+ default:
+ Item.label += C.Text;
+ break;
+ }
+ }
+ assert(CCS->getTypedText());
+ Item.kind = getKind(Result.CursorKind);
+ // Priority is a 16-bit integer, hence at most 5 digits.
+ // Since identifiers with higher priority need to come first,
+ // we subtract the priority from 99999.
+ // For example, the sort text of the identifier 'a' with priority 35
+ // is 99964a.
+ assert(CCS->getPriority() < 99999 && "Expecting code completion result "
+ "priority to have at most "
+ "5-digits");
+ llvm::raw_string_ostream(Item.sortText) << llvm::format(
+ "%05d%s", 99999 - CCS->getPriority(), CCS->getTypedText());
+ Item.insertText = Item.filterText = CCS->getTypedText();
+ if (CCS->getBriefComment())
+ Item.documentation = CCS->getBriefComment();
+ Items->push_back(std::move(Item));
+ }
+ }
+ }
+
+ GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
+
+ CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
+};
+} // namespace
+
+std::vector<CompletionItem>
+ClangdUnit::codeComplete(StringRef Contents, Position Pos,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+ CodeCompleteOptions CCO;
+ CCO.IncludeBriefComments = 1;
+ // This is where code completion stores dirty buffers. Need to free after
+ // completion.
+ SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
+ SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
+ IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
+ new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
+ std::vector<CompletionItem> Items;
+ CompletionItemsCollector Collector(&Items, CCO);
+
+ ASTUnit::RemappedFile RemappedSource(
+ FileName,
+ llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+ IntrusiveRefCntPtr<FileManager> FileMgr(
+ new FileManager(Unit->getFileSystemOpts(), VFS));
+ IntrusiveRefCntPtr<SourceManager> SourceMgr(
+ new SourceManager(*DiagEngine, *FileMgr));
+ // CodeComplete seems to require fresh LangOptions.
+ LangOptions LangOpts = Unit->getLangOpts();
+ // The language server protocol uses zero-based line and column numbers.
+ // The clang code completion uses one-based numbers.
+ Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
+ CCO.IncludeMacros, CCO.IncludeCodePatterns,
+ CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
+ LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
+ OwnedBuffers);
+ for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
+ delete Buffer;
+ return Items;
+}
+
+namespace {
+/// Convert from clang diagnostic level to LSP severity.
+static int getSeverity(DiagnosticsEngine::Level L) {
+ switch (L) {
+ case DiagnosticsEngine::Remark:
+ return 4;
+ case DiagnosticsEngine::Note:
+ return 3;
+ case DiagnosticsEngine::Warning:
+ return 2;
+ case DiagnosticsEngine::Fatal:
+ case DiagnosticsEngine::Error:
+ return 1;
+ case DiagnosticsEngine::Ignored:
+ return 0;
+ }
+ llvm_unreachable("Unknown diagnostic level!");
+}
+} // namespace
+
+std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
+ std::vector<DiagWithFixIts> Result;
+ for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
+ DEnd = Unit->stored_diag_end();
+ D != DEnd; ++D) {
+ if (!D->getLocation().isValid() ||
+ !D->getLocation().getManager().isInMainFile(D->getLocation()))
+ continue;
+ Position P;
+ P.line = D->getLocation().getSpellingLineNumber() - 1;
+ P.character = D->getLocation().getSpellingColumnNumber();
+ Range R = {P, P};
+ clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
+
+ llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
+ for (const FixItHint &Fix : D->getFixIts()) {
+ FixItsForDiagnostic.push_back(clang::tooling::Replacement(
+ Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
+ }
+ Result.push_back({Diag, std::move(FixItsForDiagnostic)});
+ }
+ return Result;
+}
+
+void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
+ Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
+}
+
+namespace {
+/// Finds declarations locations that a given source location refers to.
+class DeclarationLocationsFinder : public index::IndexDataConsumer {
+ std::vector<Location> DeclarationLocations;
+ const SourceLocation &SearchedLocation;
+ ASTUnit &Unit;
+public:
+ DeclarationLocationsFinder(raw_ostream &OS,
+ const SourceLocation &SearchedLocation, ASTUnit &Unit) :
+ SearchedLocation(SearchedLocation), Unit(Unit) {}
+
+ std::vector<Location> takeLocations() {
+ // Don't keep the same location multiple times.
+ // This can happen when nodes in the AST are visited twice.
+ std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
+ auto last =
+ std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
+ DeclarationLocations.erase(last, DeclarationLocations.end());
+ return std::move(DeclarationLocations);
+ }
+
+ bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles,
+ ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
+ index::IndexDataConsumer::ASTNodeInfo ASTNode) override
+ {
+ if (isSearchedLocation(FID, Offset)) {
+ addDeclarationLocation(D->getSourceRange());
+ }
+ return true;
+ }
+
+private:
+ bool isSearchedLocation(FileID FID, unsigned Offset) const {
+ const SourceManager &SourceMgr = Unit.getSourceManager();
+ return SourceMgr.getFileOffset(SearchedLocation) == Offset
+ && SourceMgr.getFileID(SearchedLocation) == FID;
+ }
+
+ void addDeclarationLocation(const SourceRange& ValSourceRange) {
+ const SourceManager& SourceMgr = Unit.getSourceManager();
+ const LangOptions& LangOpts = Unit.getLangOpts();
+ SourceLocation LocStart = ValSourceRange.getBegin();
+ SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
+ 0, SourceMgr, LangOpts);
+ Position Begin;
+ Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+ Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+ Position End;
+ End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+ End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+ Range R = {Begin, End};
+ Location L;
+ L.uri = URI::fromFile(
+ SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
+ L.range = R;
+ DeclarationLocations.push_back(L);
+ }
+
+ void finish() override {
+ // Also handle possible macro at the searched location.
+ Token Result;
+ if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(),
+ Unit.getASTContext().getLangOpts(), false)) {
+ if (Result.is(tok::raw_identifier)) {
+ Unit.getPreprocessor().LookUpIdentifierInfo(Result);
+ }
+ IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo();
+ if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
+ std::pair<FileID, unsigned int> DecLoc =
+ Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
+ // Get the definition just before the searched location so that a macro
+ // referenced in a '#undef MACRO' can still be found.
+ SourceLocation BeforeSearchedLocation = Unit.getLocation(
+ Unit.getSourceManager().getFileEntryForID(DecLoc.first),
+ DecLoc.second - 1);
+ MacroDefinition MacroDef =
+ Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo,
+ BeforeSearchedLocation);
+ MacroInfo* MacroInf = MacroDef.getMacroInfo();
+ if (MacroInf) {
+ addDeclarationLocation(
+ SourceRange(MacroInf->getDefinitionLoc(),
+ MacroInf->getDefinitionEndLoc()));
+ }
+ }
+ }
+ }
+};
+} // namespace
+
+std::vector<Location> ClangdUnit::findDefinitions(Position Pos) {
+ const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName());
+ if (!FE)
+ return {};
+
+ SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE);
+
+ auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
+ llvm::errs(), SourceLocationBeg, *Unit);
+ index::IndexingOptions IndexOpts;
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::All;
+ IndexOpts.IndexFunctionLocals = true;
+ index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts);
+
+ return DeclLocationsFinder->takeLocations();
+}
+
+SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos,
+ const FileEntry *FE) const {
+ // The language server protocol uses zero-based line and column numbers.
+ // Clang uses one-based numbers.
+ SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1,
+ Pos.character + 1);
+
+ if (Pos.character == 0) {
+ return InputLocation;
+ }
+
+ // This handle cases where the position is in the middle of a token or right
+ // after the end of a token. In theory we could just use GetBeginningOfToken
+ // to find the start of the token at the input position, but this doesn't
+ // work when right after the end, i.e. foo|.
+ // So try to go back by one and see if we're still inside the an identifier
+ // token. If so, Take the beginning of this token.
+ // (It should be the same identifier because you can't have two adjacent
+ // identifiers without another token in between.)
+ SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1,
+ Pos.character);
+ const SourceManager &SourceMgr = Unit->getSourceManager();
+ Token Result;
+ if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
+ Unit->getASTContext().getLangOpts(), false)) {
+ // getRawToken failed, just use InputLocation.
+ return InputLocation;
+ }
+
+ if (Result.is(tok::raw_identifier)) {
+ return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
+ Unit->getASTContext().getLangOpts());
+ }
+
+ return InputLocation;
+}
--- /dev/null
+//===--- ClangdUnit.h -------------------------------------------*- 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_CLANGD_CLANGDUNIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
+
+#include "Path.h"
+#include "Protocol.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <memory>
+
+namespace llvm {
+class raw_ostream;
+}
+
+namespace clang {
+class ASTUnit;
+class PCHContainerOperations;
+
+namespace vfs {
+class FileSystem;
+}
+
+namespace tooling {
+struct CompileCommand;
+}
+
+namespace clangd {
+
+/// A diagnostic with its FixIts.
+struct DiagWithFixIts {
+ clangd::Diagnostic Diag;
+ llvm::SmallVector<tooling::Replacement, 1> FixIts;
+};
+
+/// Stores parsed C++ AST and provides implementations of all operations clangd
+/// would want to perform on parsed C++ files.
+class ClangdUnit {
+public:
+ ClangdUnit(PathRef FileName, StringRef Contents, StringRef ResourceDir,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ std::vector<tooling::CompileCommand> Commands,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+
+ /// Reparse with new contents.
+ void reparse(StringRef Contents, IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+
+ /// Get code completions at a specified \p Line and \p Column in \p File.
+ ///
+ /// This function is thread-safe and returns completion items that own the
+ /// data they contain.
+ std::vector<CompletionItem>
+ codeComplete(StringRef Contents, Position Pos,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+ /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+ std::vector<Location> findDefinitions(Position Pos);
+ /// Returns diagnostics and corresponding FixIts for each diagnostic that are
+ /// located in the current file.
+ std::vector<DiagWithFixIts> getLocalDiagnostics() const;
+
+ /// For testing/debugging purposes. Note that this method deserializes all
+ /// unserialized Decls, so use with care.
+ void dumpAST(llvm::raw_ostream &OS) const;
+
+private:
+ Path FileName;
+ std::unique_ptr<ASTUnit> Unit;
+ std::shared_ptr<PCHContainerOperations> PCHs;
+
+ SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const;
+};
+
+} // namespace clangd
+} // namespace clang
+#endif
--- /dev/null
+//===--- ClangdUnitStore.cpp - A ClangdUnits container -----------*-C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdUnitStore.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang::clangd;
+using namespace clang;
+
+void ClangdUnitStore::removeUnitIfPresent(PathRef File) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto It = OpenedFiles.find(File);
+ if (It == OpenedFiles.end())
+ return;
+ OpenedFiles.erase(It);
+}
+
+std::vector<tooling::CompileCommand>
+ClangdUnitStore::getCompileCommands(GlobalCompilationDatabase &CDB,
+ PathRef File) {
+ std::vector<tooling::CompileCommand> Commands = CDB.getCompileCommands(File);
+ if (Commands.empty())
+ // Add a fake command line if we know nothing.
+ Commands.push_back(getDefaultCompileCommand(File));
+ return Commands;
+}
--- /dev/null
+//===--- ClangdUnitStore.h - A ClangdUnits container -------------*-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_CLANGD_CLANGDUNITSTORE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
+
+#include <mutex>
+
+#include "ClangdUnit.h"
+#include "GlobalCompilationDatabase.h"
+#include "Path.h"
+#include "clang/Tooling/CompilationDatabase.h"
+
+namespace clang {
+namespace clangd {
+
+/// Thread-safe collection of ASTs built for specific files. Provides
+/// synchronized access to ASTs.
+class ClangdUnitStore {
+public:
+ /// Run specified \p Action on the ClangdUnit for \p File.
+ /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
+ /// created from the \p FileContents. If the file is already present in the
+ /// store, ClangdUnit::reparse will be called with the new contents before
+ /// running \p Action.
+ template <class Func>
+ void runOnUnit(PathRef File, StringRef FileContents, StringRef ResourceDir,
+ GlobalCompilationDatabase &CDB,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
+ runOnUnitImpl(File, FileContents, ResourceDir, CDB, PCHs,
+ /*ReparseBeforeAction=*/true, VFS,
+ std::forward<Func>(Action));
+ }
+
+ /// Run specified \p Action on the ClangdUnit for \p File.
+ /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
+ /// created from the \p FileContents. If the file is already present in the
+ /// store, the \p Action will be run directly on it.
+ template <class Func>
+ void runOnUnitWithoutReparse(PathRef File, StringRef FileContents,
+ StringRef ResourceDir,
+ GlobalCompilationDatabase &CDB,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+ Func Action) {
+ runOnUnitImpl(File, FileContents, ResourceDir, CDB, PCHs,
+ /*ReparseBeforeAction=*/false, VFS,
+ std::forward<Func>(Action));
+ }
+
+ /// Run the specified \p Action on the ClangdUnit for \p File.
+ /// Unit for \p File should exist in the store.
+ template <class Func> void runOnExistingUnit(PathRef File, Func Action) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto It = OpenedFiles.find(File);
+ assert(It != OpenedFiles.end() && "File is not in OpenedFiles");
+
+ Action(It->second);
+ }
+
+ /// Remove ClangdUnit for \p File, if any
+ void removeUnitIfPresent(PathRef File);
+
+private:
+ /// Run specified \p Action on the ClangdUnit for \p File.
+ template <class Func>
+ void runOnUnitImpl(PathRef File, StringRef FileContents,
+ StringRef ResourceDir, GlobalCompilationDatabase &CDB,
+ std::shared_ptr<PCHContainerOperations> PCHs,
+ bool ReparseBeforeAction,
+ IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto Commands = getCompileCommands(CDB, File);
+ assert(!Commands.empty() &&
+ "getCompileCommands should add default command");
+ VFS->setCurrentWorkingDirectory(Commands.front().Directory);
+
+ auto It = OpenedFiles.find(File);
+ if (It == OpenedFiles.end()) {
+ It = OpenedFiles
+ .insert(std::make_pair(File, ClangdUnit(File, FileContents,
+ ResourceDir, PCHs,
+ Commands, VFS)))
+ .first;
+ } else if (ReparseBeforeAction) {
+ It->second.reparse(FileContents, VFS);
+ }
+ return Action(It->second);
+ }
+
+ std::vector<tooling::CompileCommand>
+ getCompileCommands(GlobalCompilationDatabase &CDB, PathRef File);
+
+ std::mutex Mutex;
+ llvm::StringMap<ClangdUnit> OpenedFiles;
+};
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DraftStore.h"
+
+using namespace clang;
+using namespace clang::clangd;
+
+VersionedDraft DraftStore::getDraft(PathRef File) const {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto It = Drafts.find(File);
+ if (It == Drafts.end())
+ return {0, llvm::None};
+ return It->second;
+}
+
+DocVersion DraftStore::getVersion(PathRef File) const {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto It = Drafts.find(File);
+ if (It == Drafts.end())
+ return 0;
+ return It->second.Version;
+}
+
+DocVersion DraftStore::updateDraft(PathRef File, StringRef Contents) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto &Entry = Drafts[File];
+ DocVersion NewVersion = ++Entry.Version;
+ Entry.Draft = Contents;
+ return NewVersion;
+}
+
+DocVersion DraftStore::removeDraft(PathRef File) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ auto &Entry = Drafts[File];
+ DocVersion NewVersion = ++Entry.Version;
+ Entry.Draft = llvm::None;
+ return NewVersion;
+}
--- /dev/null
+//===--- DraftStore.h - File contents container -----------------*- 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_CLANGD_DRAFTSTORE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
+
+#include "Path.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+/// Using 'unsigned' here to avoid undefined behaviour on overflow.
+typedef unsigned DocVersion;
+
+/// Document draft with a version of this draft.
+struct VersionedDraft {
+ DocVersion Version;
+ /// If the value of the field is None, draft is now deleted
+ llvm::Optional<std::string> Draft;
+};
+
+/// A thread-safe container for files opened in a workspace, addressed by
+/// filenames. The contents are owned by the DraftStore. Versions are mantained
+/// for the all added documents, including removed ones. The document version is
+/// incremented on each update and removal of the document.
+class DraftStore {
+public:
+ /// \return version and contents of the stored document.
+ /// For untracked files, a (0, None) pair is returned.
+ VersionedDraft getDraft(PathRef File) const;
+ /// \return version of the tracked document.
+ /// For untracked files, 0 is returned.
+ DocVersion getVersion(PathRef File) const;
+
+ /// Replace contents of the draft for \p File with \p Contents.
+ /// \return The new version of the draft for \p File.
+ DocVersion updateDraft(PathRef File, StringRef Contents);
+ /// Remove the contents of the draft
+ /// \return The new version of the draft for \p File.
+ DocVersion removeDraft(PathRef File);
+
+private:
+ mutable std::mutex Mutex;
+ llvm::StringMap<VersionedDraft> Drafts;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- GlobalCompilationDatabase.cpp --------------------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "GlobalCompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace clangd {
+
+static void addExtraFlags(tooling::CompileCommand &Command,
+ const std::vector<std::string> &ExtraFlags) {
+ if (ExtraFlags.empty())
+ return;
+ assert(Command.CommandLine.size() >= 2 &&
+ "Expected a command line containing at least 2 arguments, the "
+ "compiler binary and the output file");
+ // The last argument of CommandLine is the name of the input file.
+ // Add ExtraFlags before it.
+ auto It = Command.CommandLine.end();
+ --It;
+ Command.CommandLine.insert(It, ExtraFlags.begin(), ExtraFlags.end());
+}
+
+tooling::CompileCommand getDefaultCompileCommand(PathRef File) {
+ std::vector<std::string> CommandLine{"clang", "-fsyntax-only", File.str()};
+ return tooling::CompileCommand(llvm::sys::path::parent_path(File),
+ llvm::sys::path::filename(File), CommandLine,
+ /*Output=*/"");
+}
+
+std::vector<tooling::CompileCommand>
+DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) {
+ std::vector<tooling::CompileCommand> Commands;
+
+ auto CDB = getCompilationDatabase(File);
+ if (CDB)
+ Commands = CDB->getCompileCommands(File);
+ if (Commands.empty())
+ Commands.push_back(getDefaultCompileCommand(File));
+
+ auto It = ExtraFlagsForFile.find(File);
+ if (It != ExtraFlagsForFile.end()) {
+ // Append the user-specified flags to the compile commands.
+ for (tooling::CompileCommand &Command : Commands)
+ addExtraFlags(Command, It->second);
+ }
+
+ return Commands;
+}
+
+void DirectoryBasedGlobalCompilationDatabase::setExtraFlagsForFile(
+ PathRef File, std::vector<std::string> ExtraFlags) {
+ ExtraFlagsForFile[File] = std::move(ExtraFlags);
+}
+
+tooling::CompilationDatabase *
+DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ namespace path = llvm::sys::path;
+
+ assert((path::is_absolute(File, path::Style::posix) ||
+ path::is_absolute(File, path::Style::windows)) &&
+ "path must be absolute");
+
+ for (auto Path = path::parent_path(File); !Path.empty();
+ Path = path::parent_path(Path)) {
+
+ auto CachedIt = CompilationDatabases.find(Path);
+ if (CachedIt != CompilationDatabases.end())
+ return CachedIt->second.get();
+ std::string Error;
+ auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error);
+ if (!CDB) {
+ if (!Error.empty()) {
+ // FIXME(ibiryukov): logging
+ // Output.log("Error when trying to load compilation database from " +
+ // Twine(Path) + ": " + Twine(Error) + "\n");
+ }
+ continue;
+ }
+
+ // FIXME(ibiryukov): Invalidate cached compilation databases on changes
+ auto result = CDB.get();
+ CompilationDatabases.insert(std::make_pair(Path, std::move(CDB)));
+ return result;
+ }
+
+ // FIXME(ibiryukov): logging
+ // Output.log("Failed to find compilation database for " + Twine(File) +
+ // "\n");
+ return nullptr;
+}
+
+} // namespace clangd
+} // namespace clang
--- /dev/null
+//===--- GlobalCompilationDatabase.h ----------------------------*- 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_CLANGD_GLOBALCOMPILATIONDATABASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H
+
+#include "Path.h"
+#include "llvm/ADT/StringMap.h"
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace clang {
+
+namespace tooling {
+class CompilationDatabase;
+struct CompileCommand;
+} // namespace tooling
+
+namespace clangd {
+
+/// Returns a default compile command to use for \p File.
+tooling::CompileCommand getDefaultCompileCommand(PathRef File);
+
+/// Provides compilation arguments used for building ClangdUnit.
+class GlobalCompilationDatabase {
+public:
+ virtual ~GlobalCompilationDatabase() = default;
+
+ virtual std::vector<tooling::CompileCommand>
+ getCompileCommands(PathRef File) = 0;
+
+ /// FIXME(ibiryukov): add facilities to track changes to compilation flags of
+ /// existing targets.
+};
+
+/// Gets compile args from tooling::CompilationDatabases built for parent
+/// directories.
+class DirectoryBasedGlobalCompilationDatabase
+ : public GlobalCompilationDatabase {
+public:
+ std::vector<tooling::CompileCommand>
+ getCompileCommands(PathRef File) override;
+
+ void setExtraFlagsForFile(PathRef File, std::vector<std::string> ExtraFlags);
+
+private:
+ tooling::CompilationDatabase *getCompilationDatabase(PathRef File);
+
+ std::mutex Mutex;
+ /// Caches compilation databases loaded from directories(keys are
+ /// directories).
+ llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>>
+ CompilationDatabases;
+
+ /// Stores extra flags per file.
+ llvm::StringMap<std::vector<std::string>> ExtraFlagsForFile;
+};
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSONRPCDispatcher.h"
+#include "ProtocolHandlers.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include <istream>
+
+using namespace clang;
+using namespace clangd;
+
+void JSONOutput::writeMessage(const Twine &Message) {
+ llvm::SmallString<128> Storage;
+ StringRef M = Message.toStringRef(Storage);
+
+ std::lock_guard<std::mutex> Guard(StreamMutex);
+ // Log without headers.
+ Logs << "--> " << M << '\n';
+ Logs.flush();
+
+ // Emit message with header.
+ Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
+ Outs.flush();
+}
+
+void JSONOutput::log(const Twine &Message) {
+ std::lock_guard<std::mutex> Guard(StreamMutex);
+ Logs << Message;
+ Logs.flush();
+}
+
+void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
+ Output.log("Method ignored.\n");
+ // Return that this method is unsupported.
+ writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID +
+ R"(,"error":{"code":-32601}})");
+}
+
+void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
+ Output.log("Notification ignored.\n");
+}
+
+void JSONRPCDispatcher::registerHandler(StringRef Method,
+ std::unique_ptr<Handler> H) {
+ assert(!Handlers.count(Method) && "Handler already registered!");
+ Handlers[Method] = std::move(H);
+}
+
+static void
+callHandler(const llvm::StringMap<std::unique_ptr<Handler>> &Handlers,
+ llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
+ llvm::yaml::MappingNode *Params, Handler *UnknownHandler) {
+ llvm::SmallString<10> MethodStorage;
+ auto I = Handlers.find(Method->getValue(MethodStorage));
+ auto *Handler = I != Handlers.end() ? I->second.get() : UnknownHandler;
+ if (Id)
+ Handler->handleMethod(Params, Id->getRawValue());
+ else
+ Handler->handleNotification(Params);
+}
+
+bool JSONRPCDispatcher::call(StringRef Content) const {
+ llvm::SourceMgr SM;
+ llvm::yaml::Stream YAMLStream(Content, SM);
+
+ auto Doc = YAMLStream.begin();
+ if (Doc == YAMLStream.end())
+ return false;
+
+ auto *Root = Doc->getRoot();
+ if (!Root)
+ return false;
+
+ auto *Object = dyn_cast<llvm::yaml::MappingNode>(Root);
+ if (!Object)
+ return false;
+
+ llvm::yaml::ScalarNode *Version = nullptr;
+ llvm::yaml::ScalarNode *Method = nullptr;
+ llvm::yaml::MappingNode *Params = nullptr;
+ llvm::yaml::ScalarNode *Id = nullptr;
+ for (auto &NextKeyValue : *Object) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return false;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ llvm::yaml::Node *Value = NextKeyValue.getValue();
+ if (!Value)
+ return false;
+
+ if (KeyValue == "jsonrpc") {
+ // This should be "2.0". Always.
+ Version = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!Version || Version->getRawValue() != "\"2.0\"")
+ return false;
+ } else if (KeyValue == "method") {
+ Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ } else if (KeyValue == "id") {
+ Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ } else if (KeyValue == "params") {
+ if (!Method)
+ return false;
+ // We have to interleave the call of the function here, otherwise the
+ // YAMLParser will die because it can't go backwards. This is unfortunate
+ // because it will break clients that put the id after params. A possible
+ // fix would be to split the parsing and execution phases.
+ Params = dyn_cast<llvm::yaml::MappingNode>(Value);
+ callHandler(Handlers, Method, Id, Params, UnknownHandler.get());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // In case there was a request with no params, call the handler on the
+ // leftovers.
+ if (!Method)
+ return false;
+ callHandler(Handlers, Method, Id, nullptr, UnknownHandler.get());
+
+ return true;
+}
+
+void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
+ JSONRPCDispatcher &Dispatcher,
+ bool &IsDone) {
+ while (In.good()) {
+ // A Language Server Protocol message starts with a HTTP header, delimited
+ // by \r\n.
+ std::string Line;
+ std::getline(In, Line);
+ if (!In.good() && errno == EINTR) {
+ In.clear();
+ continue;
+ }
+
+ // Skip empty lines.
+ llvm::StringRef LineRef(Line);
+ if (LineRef.trim().empty())
+ continue;
+
+ // We allow YAML-style comments. Technically this isn't part of the
+ // LSP specification, but makes writing tests easier.
+ if (LineRef.startswith("#"))
+ continue;
+
+ unsigned long long Len = 0;
+ // FIXME: Content-Type is a specified header, but does nothing.
+ // Content-Length is a mandatory header. It specifies the length of the
+ // following JSON.
+ if (LineRef.consume_front("Content-Length: "))
+ llvm::getAsUnsignedInteger(LineRef.trim(), 0, Len);
+
+ // Check if the next line only contains \r\n. If not this is another header,
+ // which we ignore.
+ char NewlineBuf[2];
+ In.read(NewlineBuf, 2);
+ if (std::memcmp(NewlineBuf, "\r\n", 2) != 0)
+ continue;
+
+ // Now read the JSON. Insert a trailing null byte as required by the YAML
+ // parser.
+ std::vector<char> JSON(Len + 1, '\0');
+ In.read(JSON.data(), Len);
+
+ if (Len > 0) {
+ llvm::StringRef JSONRef(JSON.data(), Len);
+ // Log the message.
+ Out.log("<-- " + JSONRef + "\n");
+
+ // Finally, execute the action for this JSON message.
+ if (!Dispatcher.call(JSONRef))
+ Out.log("JSON dispatch failed!\n");
+
+ // If we're done, exit the loop.
+ if (IsDone)
+ break;
+ }
+ }
+}
--- /dev/null
+//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- 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_CLANGD_JSONRPCDISPATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/YAMLParser.h"
+#include <iosfwd>
+#include <mutex>
+
+namespace clang {
+namespace clangd {
+
+/// Encapsulates output and logs streams and provides thread-safe access to
+/// them.
+class JSONOutput {
+public:
+ JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
+ : Outs(Outs), Logs(Logs) {}
+
+ /// Emit a JSONRPC message.
+ void writeMessage(const Twine &Message);
+
+ /// Write to the logging stream.
+ void log(const Twine &Message);
+
+private:
+ llvm::raw_ostream &Outs;
+ llvm::raw_ostream &Logs;
+
+ std::mutex StreamMutex;
+};
+
+/// Callback for messages sent to the server, called by the JSONRPCDispatcher.
+class Handler {
+public:
+ Handler(JSONOutput &Output) : Output(Output) {}
+ virtual ~Handler() = default;
+
+ /// Called when the server receives a method call. This is supposed to return
+ /// a result on Outs. The default implementation returns an "unknown method"
+ /// error to the client and logs a warning.
+ virtual void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID);
+ /// Called when the server receives a notification. No result should be
+ /// written to Outs. The default implemetation logs a warning.
+ virtual void handleNotification(llvm::yaml::MappingNode *Params);
+
+protected:
+ JSONOutput &Output;
+
+ /// Helper to write a JSONRPC result to Output.
+ void writeMessage(const Twine &Message) { Output.writeMessage(Message); }
+};
+
+/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
+/// registered Handler for the method received.
+class JSONRPCDispatcher {
+public:
+ /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
+ /// method is received.
+ JSONRPCDispatcher(std::unique_ptr<Handler> UnknownHandler)
+ : UnknownHandler(std::move(UnknownHandler)) {}
+
+ /// Registers a Handler for the specified Method.
+ void registerHandler(StringRef Method, std::unique_ptr<Handler> H);
+
+ /// Parses a JSONRPC message and calls the Handler for it.
+ bool call(StringRef Content) const;
+
+private:
+ llvm::StringMap<std::unique_ptr<Handler>> Handlers;
+ std::unique_ptr<Handler> UnknownHandler;
+};
+
+/// Parses input queries from LSP client (coming from \p In) and runs call
+/// method of \p Dispatcher for each query.
+/// After handling each query checks if \p IsDone is set true and exits the loop
+/// if it is.
+/// Input stream(\p In) must be opened in binary mode to avoid preliminary
+/// replacements of \r\n with \n.
+void runLanguageServerLoop(std::istream &In, JSONOutput &Out,
+ JSONRPCDispatcher &Dispatcher, bool &IsDone);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- Path.h - Helper typedefs --------------------------------*- 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_CLANGD_PATH_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATH_H
+
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+/// A typedef to represent a file path. Used solely for more descriptive
+/// signatures.
+using Path = std::string;
+/// A typedef to represent a ref to file path. Used solely for more descriptive
+/// signatures.
+using PathRef = llvm::StringRef;
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the serialization code for the LSP structs.
+// FIXME: This is extremely repetetive and ugly. Is there a better way?
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Path.h"
+using namespace clang::clangd;
+
+
+URI URI::fromUri(llvm::StringRef uri) {
+ URI Result;
+ Result.uri = uri;
+ uri.consume_front("file://");
+ // Also trim authority-less URIs
+ uri.consume_front("file:");
+ // For Windows paths e.g. /X:
+ if (uri.size() > 2 && uri[0] == '/' && uri[2] == ':')
+ uri.consume_front("/");
+ // Make sure that file paths are in native separators
+ Result.file = llvm::sys::path::convert_to_slash(uri);
+ return Result;
+}
+
+URI URI::fromFile(llvm::StringRef file) {
+ using namespace llvm::sys;
+ URI Result;
+ Result.file = file;
+ Result.uri = "file://";
+ // For Windows paths e.g. X:
+ if (file.size() > 1 && file[1] == ':')
+ Result.uri += "/";
+ // Make sure that uri paths are with posix separators
+ Result.uri += path::convert_to_slash(file, path::Style::posix);
+ return Result;
+}
+
+URI URI::parse(llvm::yaml::ScalarNode *Param) {
+ llvm::SmallString<10> Storage;
+ return URI::fromUri(Param->getValue(Storage));
+}
+
+std::string URI::unparse(const URI &U) {
+ return "\"" + U.uri + "\"";
+}
+
+llvm::Optional<TextDocumentIdentifier>
+TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentIdentifier Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ if (KeyValue == "uri") {
+ Result.uri = URI::parse(Value);
+ } else if (KeyValue == "version") {
+ // FIXME: parse version, but only for VersionedTextDocumentIdentifiers.
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<Position> Position::parse(llvm::yaml::MappingNode *Params) {
+ Position Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "line") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.line = Val;
+ } else if (KeyValue == "character") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.character = Val;
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string Position::unparse(const Position &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result)
+ << llvm::format(R"({"line": %d, "character": %d})", P.line, P.character);
+ return Result;
+}
+
+llvm::Optional<Range> Range::parse(llvm::yaml::MappingNode *Params) {
+ Range Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "start") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.start = std::move(*Parsed);
+ } else if (KeyValue == "end") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.end = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string Range::unparse(const Range &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"start": %s, "end": %s})", Position::unparse(P.start).c_str(),
+ Position::unparse(P.end).c_str());
+ return Result;
+}
+
+std::string Location::unparse(const Location &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
+ Range::unparse(P.range).c_str());
+ return Result;
+}
+
+llvm::Optional<TextDocumentItem>
+TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentItem Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "uri") {
+ Result.uri = URI::parse(Value);
+ } else if (KeyValue == "languageId") {
+ Result.languageId = Value->getValue(Storage);
+ } else if (KeyValue == "version") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.version = Val;
+ } else if (KeyValue == "text") {
+ Result.text = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<Metadata> Metadata::parse(llvm::yaml::MappingNode *Params) {
+ Metadata Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "extraFlags") {
+ auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+ if (!Seq)
+ return llvm::None;
+ for (auto &Item : *Seq) {
+ auto *Node = dyn_cast<llvm::yaml::ScalarNode>(&Item);
+ if (!Node)
+ return llvm::None;
+ Result.extraFlags.push_back(Node->getValue(Storage));
+ }
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<TextEdit> TextEdit::parse(llvm::yaml::MappingNode *Params) {
+ TextEdit Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "range") {
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = Range::parse(Map);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "newText") {
+ auto *Node = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!Node)
+ return llvm::None;
+ Result.newText = Node->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string TextEdit::unparse(const TextEdit &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"range": %s, "newText": "%s"})", Range::unparse(P.range).c_str(),
+ llvm::yaml::escape(P.newText).c_str());
+ return Result;
+}
+
+llvm::Optional<DidOpenTextDocumentParams>
+DidOpenTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+ DidOpenTextDocumentParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentItem::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "metadata") {
+ auto Parsed = Metadata::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.metadata = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DidCloseTextDocumentParams>
+DidCloseTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+ DidCloseTextDocumentParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ if (KeyValue == "textDocument") {
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = TextDocumentIdentifier::parse(Map);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DidChangeTextDocumentParams>
+DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+ DidChangeTextDocumentParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+ if (!Map)
+ return llvm::None;
+ auto Parsed = TextDocumentIdentifier::parse(Map);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "contentChanges") {
+ auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+ if (!Seq)
+ return llvm::None;
+ for (auto &Item : *Seq) {
+ auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+ if (!I)
+ return llvm::None;
+ auto Parsed = TextDocumentContentChangeEvent::parse(I);
+ if (!Parsed)
+ return llvm::None;
+ Result.contentChanges.push_back(std::move(*Parsed));
+ }
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<TextDocumentContentChangeEvent>
+TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentContentChangeEvent Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "text") {
+ Result.text = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<FormattingOptions>
+FormattingOptions::parse(llvm::yaml::MappingNode *Params) {
+ FormattingOptions Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "tabSize") {
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.tabSize = Val;
+ } else if (KeyValue == "insertSpaces") {
+ long long Val;
+ StringRef Str = Value->getValue(Storage);
+ if (llvm::getAsSignedInteger(Str, 0, Val)) {
+ if (Str == "true")
+ Val = 1;
+ else if (Str == "false")
+ Val = 0;
+ else
+ return llvm::None;
+ }
+ Result.insertSpaces = Val;
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string FormattingOptions::unparse(const FormattingOptions &P) {
+ std::string Result;
+ llvm::raw_string_ostream(Result) << llvm::format(
+ R"({"tabSize": %d, "insertSpaces": %d})", P.tabSize, P.insertSpaces);
+ return Result;
+}
+
+llvm::Optional<DocumentRangeFormattingParams>
+DocumentRangeFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+ DocumentRangeFormattingParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "range") {
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "options") {
+ auto Parsed = FormattingOptions::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.options = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DocumentOnTypeFormattingParams>
+DocumentOnTypeFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+ DocumentOnTypeFormattingParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+ if (KeyValue == "ch") {
+ auto *ScalarValue =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!ScalarValue)
+ return llvm::None;
+ llvm::SmallString<10> Storage;
+ Result.ch = ScalarValue->getValue(Storage);
+ continue;
+ }
+
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "position") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.position = std::move(*Parsed);
+ } else if (KeyValue == "options") {
+ auto Parsed = FormattingOptions::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.options = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<DocumentFormattingParams>
+DocumentFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+ DocumentFormattingParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "options") {
+ auto Parsed = FormattingOptions::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.options = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<Diagnostic> Diagnostic::parse(llvm::yaml::MappingNode *Params) {
+ Diagnostic Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "range") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "severity") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ long long Val;
+ if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+ return llvm::None;
+ Result.severity = Val;
+ } else if (KeyValue == "message") {
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+ Result.message = Value->getValue(Storage);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<CodeActionContext>
+CodeActionContext::parse(llvm::yaml::MappingNode *Params) {
+ CodeActionContext Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value = NextKeyValue.getValue();
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "diagnostics") {
+ auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+ if (!Seq)
+ return llvm::None;
+ for (auto &Item : *Seq) {
+ auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+ if (!I)
+ return llvm::None;
+ auto Parsed = Diagnostic::parse(I);
+ if (!Parsed)
+ return llvm::None;
+ Result.diagnostics.push_back(std::move(*Parsed));
+ }
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<CodeActionParams>
+CodeActionParams::parse(llvm::yaml::MappingNode *Params) {
+ CodeActionParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ return llvm::None;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "range") {
+ auto Parsed = Range::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.range = std::move(*Parsed);
+ } else if (KeyValue == "context") {
+ auto Parsed = CodeActionContext::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.context = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+llvm::Optional<TextDocumentPositionParams>
+TextDocumentPositionParams::parse(llvm::yaml::MappingNode *Params) {
+ TextDocumentPositionParams Result;
+ for (auto &NextKeyValue : *Params) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return llvm::None;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ auto *Value =
+ dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+ if (!Value)
+ continue;
+
+ llvm::SmallString<10> Storage;
+ if (KeyValue == "textDocument") {
+ auto Parsed = TextDocumentIdentifier::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.textDocument = std::move(*Parsed);
+ } else if (KeyValue == "position") {
+ auto Parsed = Position::parse(Value);
+ if (!Parsed)
+ return llvm::None;
+ Result.position = std::move(*Parsed);
+ } else {
+ return llvm::None;
+ }
+ }
+ return Result;
+}
+
+std::string CompletionItem::unparse(const CompletionItem &CI) {
+ std::string Result = "{";
+ llvm::raw_string_ostream Os(Result);
+ assert(!CI.label.empty() && "completion item label is required");
+ Os << R"("label":")" << llvm::yaml::escape(CI.label) << R"(",)";
+ if (CI.kind != CompletionItemKind::Missing)
+ Os << R"("kind":)" << static_cast<int>(CI.kind) << R"(,)";
+ if (!CI.detail.empty())
+ Os << R"("detail":")" << llvm::yaml::escape(CI.detail) << R"(",)";
+ if (!CI.documentation.empty())
+ Os << R"("documentation":")" << llvm::yaml::escape(CI.documentation)
+ << R"(",)";
+ if (!CI.sortText.empty())
+ Os << R"("sortText":")" << llvm::yaml::escape(CI.sortText) << R"(",)";
+ if (!CI.filterText.empty())
+ Os << R"("filterText":")" << llvm::yaml::escape(CI.filterText) << R"(",)";
+ if (!CI.insertText.empty())
+ Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) << R"(",)";
+ if (CI.insertTextFormat != InsertTextFormat::Missing) {
+ Os << R"("insertTextFormat":")" << static_cast<int>(CI.insertTextFormat)
+ << R"(",)";
+ }
+ if (CI.textEdit)
+ Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ',';
+ if (!CI.additionalTextEdits.empty()) {
+ Os << R"("additionalTextEdits":[)";
+ for (const auto &Edit : CI.additionalTextEdits)
+ Os << TextEdit::unparse(Edit) << ",";
+ Os.flush();
+ // The list additionalTextEdits is guaranteed nonempty at this point.
+ // Replace the trailing comma with right brace.
+ Result.back() = ']';
+ }
+ Os.flush();
+ // Label is required, so Result is guaranteed to have a trailing comma.
+ Result.back() = '}';
+ return Result;
+}
--- /dev/null
+//===--- Protocol.h - Language Server Protocol Implementation ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains structs based on the LSP specification at
+// https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a parse and unparse function, that converts back and forth
+// between the struct and a JSON representation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/YAMLParser.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+struct URI {
+ std::string uri;
+ std::string file;
+
+ static URI fromUri(llvm::StringRef uri);
+ static URI fromFile(llvm::StringRef file);
+
+ static URI parse(llvm::yaml::ScalarNode *Param);
+ static std::string unparse(const URI &U);
+
+ friend bool operator==(const URI &LHS, const URI &RHS) {
+ return LHS.uri == RHS.uri;
+ }
+
+ friend bool operator!=(const URI &LHS, const URI &RHS) {
+ return !(LHS == RHS);
+ }
+
+ friend bool operator<(const URI &LHS, const URI &RHS) {
+ return LHS.uri < RHS.uri;
+ }
+};
+
+struct TextDocumentIdentifier {
+ /// The text document's URI.
+ URI uri;
+
+ static llvm::Optional<TextDocumentIdentifier>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct Position {
+ /// Line position in a document (zero-based).
+ int line;
+
+ /// Character offset on a line in a document (zero-based).
+ int character;
+
+ friend bool operator==(const Position &LHS, const Position &RHS) {
+ return std::tie(LHS.line, LHS.character) ==
+ std::tie(RHS.line, RHS.character);
+ }
+ friend bool operator<(const Position &LHS, const Position &RHS) {
+ return std::tie(LHS.line, LHS.character) <
+ std::tie(RHS.line, RHS.character);
+ }
+
+ static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params);
+ static std::string unparse(const Position &P);
+};
+
+struct Range {
+ /// The range's start position.
+ Position start;
+
+ /// The range's end position.
+ Position end;
+
+ friend bool operator==(const Range &LHS, const Range &RHS) {
+ return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end);
+ }
+ friend bool operator<(const Range &LHS, const Range &RHS) {
+ return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end);
+ }
+
+ static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params);
+ static std::string unparse(const Range &P);
+};
+
+struct Location {
+ /// The text document's URI.
+ URI uri;
+ Range range;
+
+ friend bool operator==(const Location &LHS, const Location &RHS) {
+ return LHS.uri == RHS.uri && LHS.range == RHS.range;
+ }
+
+ friend bool operator!=(const Location &LHS, const Location &RHS) {
+ return !(LHS == RHS);
+ }
+
+ friend bool operator<(const Location &LHS, const Location &RHS) {
+ return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
+ }
+
+ static std::string unparse(const Location &P);
+};
+
+struct Metadata {
+ std::vector<std::string> extraFlags;
+
+ static llvm::Optional<Metadata> parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextEdit {
+ /// The range of the text document to be manipulated. To insert
+ /// text into a document create a range where start === end.
+ Range range;
+
+ /// The string to be inserted. For delete operations use an
+ /// empty string.
+ std::string newText;
+
+ static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params);
+ static std::string unparse(const TextEdit &P);
+};
+
+struct TextDocumentItem {
+ /// The text document's URI.
+ URI uri;
+
+ /// The text document's language identifier.
+ std::string languageId;
+
+ /// The version number of this document (it will strictly increase after each
+ int version;
+
+ /// The content of the opened text document.
+ std::string text;
+
+ static llvm::Optional<TextDocumentItem>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidOpenTextDocumentParams {
+ /// The document that was opened.
+ TextDocumentItem textDocument;
+
+ /// Extension storing per-file metadata, such as compilation flags.
+ llvm::Optional<Metadata> metadata;
+
+ static llvm::Optional<DidOpenTextDocumentParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidCloseTextDocumentParams {
+ /// The document that was closed.
+ TextDocumentIdentifier textDocument;
+
+ static llvm::Optional<DidCloseTextDocumentParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextDocumentContentChangeEvent {
+ /// The new text of the document.
+ std::string text;
+
+ static llvm::Optional<TextDocumentContentChangeEvent>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidChangeTextDocumentParams {
+ /// The document that did change. The version number points
+ /// to the version after all provided content changes have
+ /// been applied.
+ TextDocumentIdentifier textDocument;
+
+ /// The actual content changes.
+ std::vector<TextDocumentContentChangeEvent> contentChanges;
+
+ static llvm::Optional<DidChangeTextDocumentParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct FormattingOptions {
+ /// Size of a tab in spaces.
+ int tabSize;
+
+ /// Prefer spaces over tabs.
+ bool insertSpaces;
+
+ static llvm::Optional<FormattingOptions>
+ parse(llvm::yaml::MappingNode *Params);
+ static std::string unparse(const FormattingOptions &P);
+};
+
+struct DocumentRangeFormattingParams {
+ /// The document to format.
+ TextDocumentIdentifier textDocument;
+
+ /// The range to format
+ Range range;
+
+ /// The format options
+ FormattingOptions options;
+
+ static llvm::Optional<DocumentRangeFormattingParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DocumentOnTypeFormattingParams {
+ /// The document to format.
+ TextDocumentIdentifier textDocument;
+
+ /// The position at which this request was sent.
+ Position position;
+
+ /// The character that has been typed.
+ std::string ch;
+
+ /// The format options.
+ FormattingOptions options;
+
+ static llvm::Optional<DocumentOnTypeFormattingParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DocumentFormattingParams {
+ /// The document to format.
+ TextDocumentIdentifier textDocument;
+
+ /// The format options
+ FormattingOptions options;
+
+ static llvm::Optional<DocumentFormattingParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct Diagnostic {
+ /// The range at which the message applies.
+ Range range;
+
+ /// The diagnostic's severity. Can be omitted. If omitted it is up to the
+ /// client to interpret diagnostics as error, warning, info or hint.
+ int severity;
+
+ /// The diagnostic's message.
+ std::string message;
+
+ friend bool operator==(const Diagnostic &LHS, const Diagnostic &RHS) {
+ return std::tie(LHS.range, LHS.severity, LHS.message) ==
+ std::tie(RHS.range, RHS.severity, RHS.message);
+ }
+ friend bool operator<(const Diagnostic &LHS, const Diagnostic &RHS) {
+ return std::tie(LHS.range, LHS.severity, LHS.message) <
+ std::tie(RHS.range, RHS.severity, RHS.message);
+ }
+
+ static llvm::Optional<Diagnostic> parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionContext {
+ /// An array of diagnostics.
+ std::vector<Diagnostic> diagnostics;
+
+ static llvm::Optional<CodeActionContext>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionParams {
+ /// The document in which the command was invoked.
+ TextDocumentIdentifier textDocument;
+
+ /// The range for which the command was invoked.
+ Range range;
+
+ /// Context carrying additional information.
+ CodeActionContext context;
+
+ static llvm::Optional<CodeActionParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextDocumentPositionParams {
+ /// The text document.
+ TextDocumentIdentifier textDocument;
+
+ /// The position inside the text document.
+ Position position;
+
+ static llvm::Optional<TextDocumentPositionParams>
+ parse(llvm::yaml::MappingNode *Params);
+};
+
+/// The kind of a completion entry.
+enum class CompletionItemKind {
+ Missing = 0,
+ Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+};
+
+/// Defines whether the insert text in a completion item should be interpreted
+/// as plain text or a snippet.
+enum class InsertTextFormat {
+ Missing = 0,
+ /// The primary text to be inserted is treated as a plain string.
+ PlainText = 1,
+ /// The primary text to be inserted is treated as a snippet.
+ ///
+ /// A snippet can define tab stops and placeholders with `$1`, `$2`
+ /// and `${3:foo}`. `$0` defines the final tab stop, it defaults to the end
+ /// of the snippet. Placeholders with equal identifiers are linked, that is
+ /// typing in one will update others too.
+ ///
+ /// See also:
+ /// https//github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
+ Snippet = 2,
+};
+
+struct CompletionItem {
+ /// The label of this completion item. By default also the text that is
+ /// inserted when selecting this completion.
+ std::string label;
+
+ /// The kind of this completion item. Based of the kind an icon is chosen by
+ /// the editor.
+ CompletionItemKind kind = CompletionItemKind::Missing;
+
+ /// A human-readable string with additional information about this item, like
+ /// type or symbol information.
+ std::string detail;
+
+ /// A human-readable string that represents a doc-comment.
+ std::string documentation;
+
+ /// A string that should be used when comparing this item with other items.
+ /// When `falsy` the label is used.
+ std::string sortText;
+
+ /// A string that should be used when filtering a set of completion items.
+ /// When `falsy` the label is used.
+ std::string filterText;
+
+ /// A string that should be inserted to a document when selecting this
+ /// completion. When `falsy` the label is used.
+ std::string insertText;
+
+ /// The format of the insert text. The format applies to both the `insertText`
+ /// property and the `newText` property of a provided `textEdit`.
+ InsertTextFormat insertTextFormat = InsertTextFormat::Missing;
+
+ /// An edit which is applied to a document when selecting this completion.
+ /// When an edit is provided `insertText` is ignored.
+ ///
+ /// Note: The range of the edit must be a single line range and it must
+ /// contain the position at which completion has been requested.
+ llvm::Optional<TextEdit> textEdit;
+
+ /// An optional array of additional text edits that are applied when selecting
+ /// this completion. Edits must not overlap with the main edit nor with
+ /// themselves.
+ std::vector<TextEdit> additionalTextEdits;
+
+ // TODO(krasimir): The following optional fields defined by the language
+ // server protocol are unsupported:
+ //
+ // command?: Command - An optional command that is executed *after* inserting
+ // this completion.
+ //
+ // data?: any - A data entry field that is preserved on a completion item
+ // between a completion and a completion resolve request.
+ static std::string unparse(const CompletionItem &P);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+//===--- ProtocolHandlers.cpp - LSP callbacks -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProtocolHandlers.h"
+#include "ClangdLSPServer.h"
+#include "ClangdServer.h"
+#include "DraftStore.h"
+using namespace clang;
+using namespace clangd;
+
+namespace {
+
+struct InitializeHandler : Handler {
+ InitializeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ Callbacks.onInitialize(ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct ShutdownHandler : Handler {
+ ShutdownHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ Callbacks.onShutdown(Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidOpenHandler : Handler {
+ TextDocumentDidOpenHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleNotification(llvm::yaml::MappingNode *Params) override {
+ auto DOTDP = DidOpenTextDocumentParams::parse(Params);
+ if (!DOTDP) {
+ Output.log("Failed to decode DidOpenTextDocumentParams!\n");
+ return;
+ }
+ Callbacks.onDocumentDidOpen(*DOTDP, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidChangeHandler : Handler {
+ TextDocumentDidChangeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleNotification(llvm::yaml::MappingNode *Params) override {
+ auto DCTDP = DidChangeTextDocumentParams::parse(Params);
+ if (!DCTDP || DCTDP->contentChanges.size() != 1) {
+ Output.log("Failed to decode DidChangeTextDocumentParams!\n");
+ return;
+ }
+
+ Callbacks.onDocumentDidChange(*DCTDP, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidCloseHandler : Handler {
+ TextDocumentDidCloseHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleNotification(llvm::yaml::MappingNode *Params) override {
+ auto DCTDP = DidCloseTextDocumentParams::parse(Params);
+ if (!DCTDP) {
+ Output.log("Failed to decode DidCloseTextDocumentParams!\n");
+ return;
+ }
+
+ Callbacks.onDocumentDidClose(*DCTDP, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentOnTypeFormattingHandler : Handler {
+ TextDocumentOnTypeFormattingHandler(JSONOutput &Output,
+ ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto DOTFP = DocumentOnTypeFormattingParams::parse(Params);
+ if (!DOTFP) {
+ Output.log("Failed to decode DocumentOnTypeFormattingParams!\n");
+ return;
+ }
+
+ Callbacks.onDocumentOnTypeFormatting(*DOTFP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentRangeFormattingHandler : Handler {
+ TextDocumentRangeFormattingHandler(JSONOutput &Output,
+ ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto DRFP = DocumentRangeFormattingParams::parse(Params);
+ if (!DRFP) {
+ Output.log("Failed to decode DocumentRangeFormattingParams!\n");
+ return;
+ }
+
+ Callbacks.onDocumentRangeFormatting(*DRFP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentFormattingHandler : Handler {
+ TextDocumentFormattingHandler(JSONOutput &Output,
+ ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto DFP = DocumentFormattingParams::parse(Params);
+ if (!DFP) {
+ Output.log("Failed to decode DocumentFormattingParams!\n");
+ return;
+ }
+
+ Callbacks.onDocumentFormatting(*DFP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct CodeActionHandler : Handler {
+ CodeActionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto CAP = CodeActionParams::parse(Params);
+ if (!CAP) {
+ Output.log("Failed to decode CodeActionParams!\n");
+ return;
+ }
+
+ Callbacks.onCodeAction(*CAP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct CompletionHandler : Handler {
+ CompletionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto TDPP = TextDocumentPositionParams::parse(Params);
+ if (!TDPP) {
+ Output.log("Failed to decode TextDocumentPositionParams!\n");
+ return;
+ }
+
+ Callbacks.onCompletion(*TDPP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+struct GotoDefinitionHandler : Handler {
+ GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+ : Handler(Output), Callbacks(Callbacks) {}
+
+ void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+ auto TDPP = TextDocumentPositionParams::parse(Params);
+ if (!TDPP) {
+ Output.log("Failed to decode TextDocumentPositionParams!\n");
+ return;
+ }
+
+ Callbacks.onGoToDefinition(*TDPP, ID, Output);
+ }
+
+private:
+ ProtocolCallbacks &Callbacks;
+};
+
+} // namespace
+
+void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
+ JSONOutput &Out,
+ ProtocolCallbacks &Callbacks) {
+ Dispatcher.registerHandler(
+ "initialize", llvm::make_unique<InitializeHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "shutdown", llvm::make_unique<ShutdownHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/didOpen",
+ llvm::make_unique<TextDocumentDidOpenHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/didClose",
+ llvm::make_unique<TextDocumentDidCloseHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/didChange",
+ llvm::make_unique<TextDocumentDidChangeHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/rangeFormatting",
+ llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/onTypeFormatting",
+ llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/formatting",
+ llvm::make_unique<TextDocumentFormattingHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/codeAction",
+ llvm::make_unique<CodeActionHandler>(Out, Callbacks));
+ Dispatcher.registerHandler(
+ "textDocument/completion",
+ llvm::make_unique<CompletionHandler>(Out, Callbacks));
+ Dispatcher.registerHandler("textDocument/definition",
+ llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
+}
--- /dev/null
+//===--- ProtocolHandlers.h - LSP callbacks ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the actions performed when the server gets a specific
+// request.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H
+
+#include "JSONRPCDispatcher.h"
+#include "Protocol.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace clangd {
+
+class ProtocolCallbacks {
+public:
+ virtual ~ProtocolCallbacks() = default;
+
+ virtual void onInitialize(StringRef ID, JSONOutput &Out) = 0;
+ virtual void onShutdown(JSONOutput &Out) = 0;
+ virtual void onDocumentDidOpen(DidOpenTextDocumentParams Params,
+ JSONOutput &Out) = 0;
+ virtual void onDocumentDidChange(DidChangeTextDocumentParams Params,
+ JSONOutput &Out) = 0;
+
+ virtual void onDocumentDidClose(DidCloseTextDocumentParams Params,
+ JSONOutput &Out) = 0;
+ virtual void onDocumentFormatting(DocumentFormattingParams Params,
+ StringRef ID, JSONOutput &Out) = 0;
+ virtual void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
+ StringRef ID, JSONOutput &Out) = 0;
+ virtual void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
+ StringRef ID, JSONOutput &Out) = 0;
+ virtual void onCodeAction(CodeActionParams Params, StringRef ID,
+ JSONOutput &Out) = 0;
+ virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) = 0;
+ virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+ JSONOutput &Out) = 0;
+};
+
+void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
+ ProtocolCallbacks &Callbacks);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
--- /dev/null
+out
+node_modules
\ No newline at end of file
--- /dev/null
+// A launch configuration that compiles the extension and then opens it inside a new window
+{
+ "version": "0.1.0",
+ "configurations": [
+ {
+ "name": "Launch Extension",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
+ "stopOnEntry": false,
+ "sourceMaps": true,
+ "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ],
+ "preLaunchTask": "npm"
+ },
+ {
+ "name": "Launch Tests",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
+ "stopOnEntry": false,
+ "sourceMaps": true,
+ "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
+ "preLaunchTask": "npm"
+ }
+ ]
+}
--- /dev/null
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.exclude": {
+ "out": false // set this to true to hide the "out" folder with the compiled JS files
+ },
+ "search.exclude": {
+ "out": true // set this to false to include "out" folder in search results
+ }
+}
\ No newline at end of file
--- /dev/null
+// Available variables which can be used inside of strings.
+// ${workspaceRoot}: the root folder of the team
+// ${file}: the current opened file
+// ${fileBasename}: the current opened file's basename
+// ${fileDirname}: the current opened file's dirname
+// ${fileExtname}: the current opened file's extension
+// ${cwd}: the current working directory of the spawned process
+
+// A task runner that calls a custom npm script that compiles the extension.
+{
+ "version": "0.1.0",
+
+ // we want to run npm
+ "command": "npm",
+
+ // the command is a shell script
+ "isShellCommand": true,
+
+ // show the output window only if unrecognized errors occur.
+ "showOutput": "silent",
+
+ // we run the custom script "compile" as defined in package.json
+ "args": ["run", "compile", "--loglevel", "silent"],
+
+ // The tsc compiler is started in watching mode
+ "isWatching": true,
+
+ // use the standard tsc in watch mode problem matcher to find compile problems in the output.
+ "problemMatcher": "$tsc-watch"
+}
\ No newline at end of file
--- /dev/null
+.vscode/**
+.vscode-test/**
+out/test/**
+test/**
+src/**
+**/*.map
+.gitignore
+tsconfig.json
+vsc-extension-quickstart.md
--- /dev/null
+A *toy* VS Code integration for development purposes.
+
+Steps:
+1. Make sure you have clangd in /usr/bin/clangd or edit src/extension.ts to
+point to the binary.
+2. Make sure you have nodejs and npm installed.
+3. Make sure you have VS Code installed.
+4. In order to start a development instance of VS code extended with this, run:
+ $ npm install
+ $ code .
+ When VS Code starts, press <F5>.
--- /dev/null
+{
+ "name": "clangd-vscode",
+ "displayName": "clangd-vscode",
+ "description": "Clang Language Server",
+ "version": "0.0.1",
+ "publisher": "Unpublished",
+ "engines": {
+ "vscode": "^1.8.0"
+ },
+ "categories": [
+ "Languages",
+ "Linters",
+ "Snippets"
+ ],
+ "activationEvents": [
+ "onLanguage:cpp",
+ "onLanguage:c"
+ ],
+ "main": "./out/src/extension",
+ "scripts": {
+ "vscode:prepublish": "tsc -p ./",
+ "compile": "tsc -watch -p ./",
+ "postinstall": "node ./node_modules/vscode/bin/install",
+ "test": "node ./node_modules/vscode/bin/test"
+ },
+ "dependencies": {
+ "vscode-languageclient": "^2.6.3",
+ "vscode-languageserver": "^2.6.2"
+ },
+ "devDependencies": {
+ "typescript": "^2.0.3",
+ "vscode": "^1.0.3",
+ "mocha": "^2.3.3",
+ "@types/node": "^6.0.40",
+ "@types/mocha": "^2.2.32"
+ },
+ "contributes": {
+ "configuration": {
+ "type": "object",
+ "title": "clangd configuration",
+ "properties": {
+ "clangd.path": {
+ "type": "string",
+ "default": "clangd",
+ "description": "The path to clangd executable, e.g.: /usr/bin/clangd"
+ },
+ "clangd.arguments": {
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "string"
+ },
+ "description": "Arguments for clangd server"
+ }
+ }
+ }
+ }
+}
--- /dev/null
+import * as vscode from 'vscode';
+import * as vscodelc from 'vscode-languageclient';
+
+/**
+ * Method to get workspace configuration option
+ * @param option name of the option (e.g. for clangd.path should be path)
+ * @param defaultValue default value to return if option is not set
+ */
+function getConfig<T>(option: string, defaultValue?: any) : T {
+ const config = vscode.workspace.getConfiguration('clangd');
+ return config.get<T>(option, defaultValue);
+}
+
+/**
+ * this method is called when your extension is activate
+ * your extension is activated the very first time the command is executed
+ */
+export function activate(context: vscode.ExtensionContext) {
+ const clangdPath = getConfig<string>('path');
+ const clangdArgs = getConfig<string[]>('arguments');
+
+ const serverOptions: vscodelc.ServerOptions = { command: clangdPath, args: clangdArgs };
+
+ const clientOptions: vscodelc.LanguageClientOptions = {
+ // Register the server for C/C++ files
+ documentSelector: ['c', 'cc', 'cpp', 'h', 'hh', 'hpp'],
+ uriConverters: {
+ // FIXME: by default the URI sent over the protocol will be percent encoded (see rfc3986#section-2.1)
+ // the "workaround" below disables temporarily the encoding until decoding
+ // is implemented properly in clangd
+ code2Protocol: (uri: vscode.Uri) : string => uri.toString(true),
+ protocol2Code: (uri: string) : vscode.Uri => vscode.Uri.parse(uri)
+ }
+ };
+
+ const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions);
+
+ function applyTextEdits(uri: string, edits: vscodelc.TextEdit[]) {
+ let textEditor = vscode.window.activeTextEditor;
+
+ // FIXME: vscode expects that uri will be percent encoded
+ if (textEditor && textEditor.document.uri.toString(true) === uri) {
+ textEditor.edit(mutator => {
+ for (const edit of edits) {
+ mutator.replace(vscodelc.Protocol2Code.asRange(edit.range), edit.newText);
+ }
+ }).then((success) => {
+ if (!success) {
+ vscode.window.showErrorMessage('Failed to apply fixes to the document.');
+ }
+ });
+ }
+ }
+
+ console.log('Clang Language Server is now active!');
+
+ const disposable = clangdClient.start();
+
+ context.subscriptions.push(disposable, vscode.commands.registerCommand('clangd.applyFix', applyTextEdits));
+}
--- /dev/null
+/** The module 'assert' provides assertion methods from node */
+import * as assert from 'assert';
+
+import * as vscode from 'vscode';
+import * as myExtension from '../src/extension';
+
+// TODO: add tests
+suite("Extension Tests", () => {
+
+ // Defines a Mocha unit test
+ test("Something 1", () => {
+ assert.equal(-1, [1, 2, 3].indexOf(5));
+ assert.equal(-1, [1, 2, 3].indexOf(0));
+ });
+});
\ No newline at end of file
--- /dev/null
+//
+// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
+//
+// This file is providing the test runner to use when running extension tests.
+// By default the test runner in use is Mocha based.
+//
+// You can provide your own test runner if you want to override it by exporting
+// a function run(testRoot: string, clb: (error:Error) => void) that the extension
+// host can call to run the tests. The test runner is expected to use console.log
+// to report the results back to the caller. When the tests are finished, return
+// a possible error to the callback or null if none.
+
+var testRunner = require('vscode/lib/testrunner');
+
+// You can directly control Mocha options by uncommenting the following lines
+// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
+testRunner.configure({
+ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
+ useColors: true // colored output from test results
+});
+
+module.exports = testRunner;
\ No newline at end of file
--- /dev/null
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es6",
+ "outDir": "out",
+ "lib": [
+ "es6",
+ "es2015.core",
+ "es2015.collection",
+ "es2015.generator",
+ "es2015.iterable",
+ "es2015.promise",
+ "es2015.symbol",
+ "es2016.array.include"
+ ],
+ "sourceMap": true,
+ "rootDir": ".",
+ "alwaysStrict": true,
+ "noEmitOnError": true,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitAny": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true
+ },
+ "exclude": [
+ "node_modules",
+ ".vscode-test"
+ ]
+}
\ No newline at end of file
--- /dev/null
+# Toy VS Code Extension for clangd
+
+## What's in the folder
+* This folder contains all of the files necessary for your extension
+* `package.json` - this is the manifest file in which you declare your extension and command.
+The sample plugin registers a command and defines its title and command name. With this information
+VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
+* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
+The file exports one function, `activate`, which is called the very first time your extension is
+activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
+We pass the function containing the implementation of the command as the second parameter to
+`registerCommand`.
+
+## Get up and running straight away
+* press `F5` to open a new window with your extension loaded
+* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
+* set breakpoints in your code inside `src/extension.ts` to debug your extension
+* find output from your extension in the debug console
+
+## Make changes
+* you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`
+* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
+
+## Explore the API
+* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
+
+## Run tests
+* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
+* press `F5` to run the tests in a new window with your extension loaded
+* see the output of the test result in the debug console
+* make changes to `test/extension.test.ts` or create new test files inside the `test` folder
+ * by convention, the test runner will only consider files matching the name pattern `**.test.ts`
+ * you can create folders inside the `test` folder to structure your tests any way you want
\ No newline at end of file
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_tool(clangd
+ ClangdMain.cpp
+ )
+
+install(TARGETS clangd RUNTIME DESTINATION bin)
+
+target_link_libraries(clangd
+ clangBasic
+ clangDaemon
+ clangFormat
+ clangFrontend
+ clangSema
+ clangTooling
+ clangToolingCore
+ LLVMSupport
+ )
--- /dev/null
+//===--- ClangdMain.cpp - clangd server loop ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdLSPServer.h"
+#include "JSONRPCDispatcher.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+using namespace clang;
+using namespace clang::clangd;
+
+static llvm::cl::opt<bool>
+ RunSynchronously("run-synchronously",
+ llvm::cl::desc("parse on main thread"),
+ llvm::cl::init(false), llvm::cl::Hidden);
+
+int main(int argc, char *argv[]) {
+ llvm::cl::ParseCommandLineOptions(argc, argv, "clangd");
+
+ llvm::raw_ostream &Outs = llvm::outs();
+ llvm::raw_ostream &Logs = llvm::errs();
+ JSONOutput Out(Outs, Logs);
+
+ // Change stdin to binary to not lose \r\n on windows.
+ llvm::sys::ChangeStdinToBinary();
+
+ ClangdLSPServer LSPServer(Out, RunSynchronously);
+ LSPServer.run(std::cin);
+}
--- /dev/null
+if (DOXYGEN_FOUND)
+ if (LLVM_ENABLE_DOXYGEN)
+ set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+ set(abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+
+ if (HAVE_DOT)
+ set(DOT ${LLVM_PATH_DOT})
+ endif()
+
+ if (LLVM_DOXYGEN_EXTERNAL_SEARCH)
+ set(enable_searchengine "YES")
+ set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}")
+ set(enable_server_based_search "YES")
+ set(enable_external_search "YES")
+ set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}")
+ else()
+ set(enable_searchengine "NO")
+ set(searchengine_url "")
+ set(enable_server_based_search "NO")
+ set(enable_external_search "NO")
+ set(extra_search_mappings "")
+ endif()
+
+ # If asked, configure doxygen for the creation of a Qt Compressed Help file.
+ if (LLVM_ENABLE_DOXYGEN_QT_HELP)
+ set(CLANG_TOOLS_DOXYGEN_QCH_FILENAME "org.llvm.clang.qch" CACHE STRING
+ "Filename of the Qt Compressed help file")
+ set(CLANG_TOOLS_DOXYGEN_QHP_NAMESPACE "org.llvm.clang" CACHE STRING
+ "Namespace under which the intermediate Qt Help Project file lives")
+ set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_NAME "Clang ${CLANG_VERSION}" CACHE STRING
+ "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters")
+ set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_ATTRS "Clang,${CLANG_VERSION}" CACHE STRING
+ "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes")
+ set(clang_tools_doxygen_generate_qhp "YES")
+ set(clang_tools_doxygen_qch_filename "${CLANG_DOXYGEN_QCH_FILENAME}")
+ set(clang_tools_doxygen_qhp_namespace "${CLANG_DOXYGEN_QHP_NAMESPACE}")
+ set(clang_tools_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}")
+ set(clang_tools_doxygen_qhp_cust_filter_name "${CLANG_DOXYGEN_QHP_CUST_FILTER_NAME}")
+ set(clang_tools_doxygen_qhp_cust_filter_attrs "${CLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS}")
+ else()
+ set(clang_tools_doxygen_generate_qhp "NO")
+ set(clang_tools_doxygen_qch_filename "")
+ set(clang_tools_doxygen_qhp_namespace "")
+ set(clang_tools_doxygen_qhelpgenerator_path "")
+ set(clang_tools_doxygen_qhp_cust_filter_name "")
+ set(clang_tools_doxygen_qhp_cust_filter_attrs "")
+ endif()
+
+ option(LLVM_DOXYGEN_SVG
+ "Use svg instead of png files for doxygen graphs." OFF)
+ if (LLVM_DOXYGEN_SVG)
+ set(DOT_IMAGE_FORMAT "svg")
+ else()
+ set(DOT_IMAGE_FORMAT "png")
+ endif()
+
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY)
+
+ set(abs_top_srcdir)
+ set(abs_top_builddir)
+ set(DOT)
+ set(enable_searchengine)
+ set(searchengine_url)
+ set(enable_server_based_search)
+ set(enable_external_search)
+ set(extra_search_mappings)
+ set(clang_tools_doxygen_generate_qhp)
+ set(clang_tools_doxygen_qch_filename)
+ set(clang_tools_doxygen_qhp_namespace)
+ set(clang_tools_doxygen_qhelpgenerator_path)
+ set(clang_tools_doxygen_qhp_cust_filter_name)
+ set(clang_tools_doxygen_qhp_cust_filter_attrs)
+ set(DOT_IMAGE_FORMAT)
+
+ add_custom_target(doxygen-clang-tools
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Generating clang doxygen documentation." VERBATIM)
+
+ if (LLVM_BUILD_DOCS)
+ add_dependencies(doxygen doxygen-clang-tools)
+ endif()
+
+ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
+ DESTINATION docs/html)
+ endif()
+ endif()
+endif()
+
+if (LLVM_ENABLE_SPHINX)
+ include(AddSphinxTarget)
+ if (SPHINX_FOUND)
+ if (${SPHINX_OUTPUT_HTML})
+ add_sphinx_target(html clang-tools)
+ endif()
+ if (${SPHINX_OUTPUT_MAN})
+ add_sphinx_target(man clang-tools)
+ endif()
+ endif()
+endif()
--- /dev/null
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = clang-tools-extra
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+# Same directory that Sphinx uses.
+OUTPUT_DIRECTORY = ./_build/
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 2
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page. This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ../clang-modernize ../clang-apply-replacements ../clang-tidy
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.d \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox \
+ *.py \
+ *.f90 \
+ *.f \
+ *.for \
+ *.vhd \
+ *.vhdl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# none of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 4
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX = clang::
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+# This directory nicely fits with the way the Sphinx outputs html, so that
+# the doxygen documentation will be visible in the doxygen/ path in the web
+# output (e.g. github pages).
+HTML_OUTPUT = html/doxygen/
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+# For now, no latex output.
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex/doxygen
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = YES
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
--- /dev/null
+================
+Modularize Usage
+================
+
+``modularize [<modularize-options>] [<module-map>|<include-files-list>]*
+[<front-end-options>...]``
+
+``<modularize-options>`` is a place-holder for options
+specific to modularize, which are described below in
+`Modularize Command Line Options`.
+
+``<module-map>`` specifies the path of a file name for an
+existing module map. The module map must be well-formed in
+terms of syntax. Modularize will extract the header file names
+from the map. Only normal headers are checked, assuming headers
+marked "private", "textual", or "exclude" are not to be checked
+as a top-level include, assuming they either are included by
+other headers which are checked, or they are not suitable for
+modules.
+
+``<include-files-list>`` specifies the path of a file name for a
+file containing the newline-separated list of headers to check
+with respect to each other. Lines beginning with '#' and empty
+lines are ignored. Header file names followed by a colon and
+other space-separated file names will include those extra files
+as dependencies. The file names can be relative or full paths,
+but must be on the same line. For example::
+
+ header1.h
+ header2.h
+ header3.h: header1.h header2.h
+
+Note that unless a ``-prefix (header path)`` option is specified,
+non-absolute file paths in the header list file will be relative
+to the header list file directory. Use -prefix to specify a different
+directory.
+
+``<front-end-options>`` is a place-holder for regular Clang
+front-end arguments, which must follow the <include-files-list>.
+Note that by default, modularize assumes .h files
+contain C++ source, so if you are using a different language,
+you might need to use a ``-x`` option to tell Clang that the
+header contains another language, i.e.: ``-x c``
+
+Note also that because modularize does not use the clang driver,
+you will likely need to pass in additional compiler front-end
+arguments to match those passed in by default by the driver.
+
+Modularize Command Line Options
+===============================
+
+.. option:: -prefix=<header-path>
+
+ Prepend the given path to non-absolute file paths in the header list file.
+ By default, headers are assumed to be relative to the header list file
+ directory. Use ``-prefix`` to specify a different directory.
+
+.. option:: -module-map-path=<module-map-path>
+
+ Generate a module map and output it to the given file. See the description
+ in :ref:`module-map-generation`.
+
+.. option:: -problem-files-list=<problem-files-list-file-name>
+
+ For use only with module map assistant. Input list of files that
+ have problems with respect to modules. These will still be
+ included in the generated module map, but will be marked as
+ "excluded" headers.
+
+.. option:: -root-module=<root-name>
+
+ Put modules generated by the -module-map-path option in an enclosing
+ module with the given name. See the description in :ref:`module-map-generation`.
+
+.. option:: -block-check-header-list-only
+
+ Limit the #include-inside-extern-or-namespace-block
+ check to only those headers explicitly listed in the header list.
+ This is a work-around for avoiding error messages for private includes that
+ purposefully get included inside blocks.
+
+.. option:: -no-coverage-check
+
+ Don't do the coverage check for a module map.
+
+.. option:: -coverage-check-only
+
+ Only do the coverage check for a module map.
+
+.. option:: -display-file-lists
+
+ Display lists of good files (no compile errors), problem files,
+ and a combined list with problem files preceded by a '#'.
+ This can be used to quickly determine which files have problems.
+ The latter combined list might be useful in starting to modularize
+ a set of headers. You can start with a full list of headers,
+ use -display-file-lists option, and then use the combined list as
+ your intermediate list, uncommenting-out headers as you fix them.
--- /dev/null
+-------------------------------------------------------------
+Documentation for the tools of clang-tools-extra repo project
+-------------------------------------------------------------
+
+Sphinx and doxygen documentation is generated by executing make.
+
+Sphinx html files can be generated separately using make html.
+
+Doxygen html files can also be generated using make doxygen.
+
+The generated documentation will be placed in _build/html.
--- /dev/null
+=====================================
+Extra Clang Tools 5.0.0 Release Notes
+=====================================
+
+.. contents::
+ :local:
+ :depth: 3
+
+Written by the `LLVM Team <http://llvm.org/>`_
+
+Introduction
+============
+
+This document contains the release notes for the Extra Clang Tools, part of the
+Clang release 5.0.0. Here we describe the status of the Extra Clang Tools in
+some detail, including major improvements from the previous release and new
+feature work. All LLVM releases may be downloaded from the `LLVM releases web
+site <http://llvm.org/releases/>`_.
+
+For more information about Clang or LLVM, including information about
+the latest release, please see the `Clang Web Site <http://clang.llvm.org>`_ or
+the `LLVM Web Site <http://llvm.org>`_.
+
+What's New in Extra Clang Tools 5.0.0?
+======================================
+
+Improvements to clang-tidy
+--------------------------
+
+- New `android-cloexec-creat
+ <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html>`_ check
+
+ Detect usage of ``creat()``.
+
+- New `android-cloexec-open
+ <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-open.html>`_ check
+
+ Checks if the required file flag ``O_CLOEXEC`` exists in ``open()``,
+ ``open64()`` and ``openat()``.
+
+- New `android-cloexec-fopen
+ <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html>`_ check
+
+ Checks if the required mode ``e`` exists in the mode argument of ``fopen()``.
+
+- New `android-cloexec-socket
+ <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html>`_ check
+
+ Checks if the required file flag ``SOCK_CLOEXEC`` is present in the argument of
+ ``socket()``.
+
+- New `bugprone-suspicious-memset-usage
+ <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memset-usage.html>`_ check
+
+ Finds ``memset()`` calls with potential mistakes in their arguments.
+ Replaces and extends the ``google-runtime-memset`` check.
+
+- New `bugprone-undefined-memory-manipulation
+ <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-undefined-memory-manipulation.html>`_ check
+
+ Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+ ``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior.
+
+- New `cert-dcl21-cpp
+ <http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl21-cpp.html>`_ check
+
+ Checks if the overloaded postfix ``operator++/--`` returns a constant object.
+
+- New `cert-dcl58-cpp
+ <http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl58-cpp.html>`_ check
+
+ Finds modification of the ``std`` or ``posix`` namespace.
+
+- Improved `cppcoreguidelines-no-malloc
+ <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html>`_ check
+
+ Allow custom memory management functions to be considered as well.
+
+- New `misc-forwarding-reference-overload
+ <http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html>`_ check
+
+ Finds perfect forwarding constructors that can unintentionally hide copy or move constructors.
+
+- New `misc-lambda-function-name <http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html>`_ check
+
+ Finds uses of ``__func__`` or ``__FUNCTION__`` inside lambdas.
+
+- New `modernize-replace-random-shuffle
+ <http://clang.llvm.org/extra/clang-tidy/checks/modernize-replace-random-shuffle.html>`_ check
+
+ Finds and fixes usage of ``std::random_shuffle`` as the function has been removed from C++17.
+
+- New `modernize-return-braced-init-list
+ <http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html>`_ check
+
+ Finds and replaces explicit calls to the constructor in a return statement by
+ a braced initializer list so that the return type is not needlessly repeated.
+
+- New `modernize-unary-static-assert-check
+ <http://clang.llvm.org/extra/clang-tidy/checks/modernize-unary-static-assert.html>`_ check
+
+ The check diagnoses any ``static_assert`` declaration with an empty string literal
+ and provides a fix-it to replace the declaration with a single-argument ``static_assert`` declaration.
+
+- Improved `modernize-use-emplace
+ <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-emplace.html>`_ check
+
+ Removes unnecessary ``std::make_pair`` and ``std::make_tuple`` calls in
+ push_back calls and turns them into emplace_back. The check now also is able
+ to remove user-defined make functions from ``push_back`` calls on containers
+ of custom tuple-like types by providing `TupleTypes` and `TupleMakeFunctions`.
+
+- New `modernize-use-noexcept
+ <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html>`_ check
+
+ Replaces dynamic exception specifications with ``noexcept`` or a user defined macro.
+
+- New `performance-inefficient-vector-operation
+ <http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html>`_ check
+
+ Finds possible inefficient vector operations in for loops that may cause
+ unnecessary memory reallocations.
+
+- Added `NestingThreshold` to `readability-function-size
+ <http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check
+
+ Finds compound statements which create next nesting level after `NestingThreshold` and emits a warning.
+
+- Added `ParameterThreshold` to `readability-function-size
+ <http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check
+
+ Finds functions that have more than `ParameterThreshold` parameters and emits a warning.
+
+- New `readability-misleading-indentation
+ <http://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html>`_ check
+
+ Finds misleading indentation where braces should be introduced or the code should be reformatted.
+
+- Support clang-formatting of the code around applied fixes (``-format-style``
+ command-line option).
+
+- New `bugprone` module
+
+ Adds checks that target bugprone code constructs.
+
+- New `hicpp` module
+
+ Adds checks that implement the `High Integrity C++ Coding Standard <http://www.codingstandard.com/section/index/>`_ and other safety
+ standards. Many checks are aliased to other modules.
--- /dev/null
+:orphan:
+
+All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
+(see the ``modernize`` module).
--- /dev/null
+============
+Clang-Rename
+============
+
+.. contents::
+
+See also:
+
+.. toctree::
+ :maxdepth: 1
+
+
+: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
+to `the LLVM bugtracker <https://llvm.org/bugs>`_ will definitely help the
+project. If you have any ideas or suggestions, you might want to put a feature
+request there.
+
+Using Clang-Rename
+==================
+
+:program:`clang-rename` is a `LibTooling
+<http://clang.llvm.org/docs/LibTooling.html>`_-based tool, and it's easier to
+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
+<http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html>`_). You can also
+specify compilation options on the command line after `--`:
+
+.. code-block:: console
+
+ $ clang-rename -offset=42 -new-name=foo test.cpp -- -Imy_project/include -DMY_DEFINES ...
+
+
+To get an offset of a symbol in a file run
+
+.. code-block:: console
+
+ $ grep -FUbo 'foo' file.cpp
+
+
+The tool currently supports renaming actions inside a single translation unit
+only. It is planned to extend the tool's functionality to support multi-TU
+renaming actions in the future.
+
+:program:`clang-rename` also aims to be easily integrated into popular text
+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 -qualified-name=foo -new-name=bar test.cpp
+
+Renaming multiple symbols at once is supported, too. However,
+:program:`clang-rename` doesn't accept both `-offset` and `-qualified-name` at
+the same time. So, you can either specify multiple `-offset` or
+`-qualified-name`.
+
+.. code-block:: console
+
+ $ clang-rename -offset=42 -new-name=bar1 -offset=150 -new-name=bar2 test.cpp
+
+or
+
+.. code-block:: console
+
+ $ clang-rename -qualified-name=foo1 -new-name=bar1 -qualified-name=foo2 -new-name=bar2 test.cpp
+
+
+Alternatively, {offset | qualified-name} / new-name pairs can be put into a YAML
+file:
+
+.. code-block:: yaml
+
+ ---
+ - Offset: 42
+ NewName: bar1
+ - Offset: 150
+ NewName: bar2
+ ...
+
+or
+
+.. code-block:: yaml
+
+ ---
+ - QualifiedName: foo1
+ NewName: bar1
+ - QualifiedName: foo2
+ NewName: bar2
+ ...
+
+That way you can avoid spelling out all the names as command line arguments:
+
+.. code-block:: console
+
+ $ clang-rename -input=test.yaml test.cpp
+
+:program:`clang-rename` offers the following options:
+
+.. code-block:: console
+
+ $ clang-rename --help
+ USAGE: clang-rename [subcommand] [options] <source0> [... <sourceN>]
+
+ 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 common options:
+
+ -export-fixes=<filename> - YAML file to store suggested fixes in.
+ -extra-arg=<string> - Additional argument to append to the compiler command line
+ -extra-arg-before=<string> - Additional argument to prepend to the compiler command line
+ -force - Ignore nonexistent qualified names.
+ -i - Overwrite edited <file>s.
+ -input=<string> - YAML file to load oldname-newname pairs from.
+ -new-name=<string> - The new name to change the symbol to.
+ -offset=<uint> - Locates the symbol by offset as opposed to <line>:<column>.
+ -p=<string> - Build path
+ -pl - Print the locations affected by renaming to stderr.
+ -pn - Print the found symbol's name prior to renaming to stderr.
+ -qualified-name=<string> - The fully qualified name of the symbol.
+
+Vim Integration
+===============
+
+You can call :program:`clang-rename` directly from Vim! To set up
+:program:`clang-rename` integration for Vim see
+`clang-rename/tool/clang-rename.py
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-rename/tool/clang-rename.py>`_.
+
+Please note that **you have to save all buffers, in which the replacement will
+happen before running the tool**.
+
+Once installed, you can point your cursor to symbols you want to rename, press
+`<leader>cr` and type new desired name. The `<leader> key
+<http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader>`_
+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
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/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**.
--- /dev/null
+:orphan:
+
+.. meta::
+ :http-equiv=refresh: 0;URL='clang-tidy/'
+
+clang-tidy documentation has moved here: http://clang.llvm.org/extra/clang-tidy/
--- /dev/null
+.. title:: clang-tidy - android-cloexec-creat
+
+android-cloexec-creat
+=====================
+
+The usage of ``creat()`` is not recommended, it's better to use ``open()``.
+
+Examples:
+
+.. code-block:: c++
+
+ int fd = creat(path, mode);
+
+ // becomes
+
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
--- /dev/null
+.. title:: clang-tidy - android-cloexec-fopen
+
+android-cloexec-fopen
+=====================
+
+``fopen()`` should include ``e`` in their mode string; so ``re`` would be
+valid. This is equivalent to having set ``FD_CLOEXEC on`` that descriptor.
+
+Examples:
+
+.. code-block:: c++
+
+ fopen("fn", "r");
+
+ // becomes
+
+ fopen("fn", "re");
+
--- /dev/null
+.. title:: clang-tidy - android-cloexec-open
+
+android-cloexec-open
+====================
+
+A common source of security bugs is code that opens a file without using the
+``O_CLOEXEC`` flag. Without that flag, an opened sensitive file would remain
+open across a fork+exec to a lower-privileged SELinux domain, leaking that
+sensitive data. Open-like functions including ``open()``, ``openat()``, and
+``open64()`` should include ``O_CLOEXEC`` in their flags argument.
+
+Examples:
+
+.. code-block:: c++
+
+ open("filename", O_RDWR);
+ open64("filename", O_RDWR);
+ openat(0, "filename", O_RDWR);
+
+ // becomes
+
+ open("filename", O_RDWR | O_CLOEXEC);
+ open64("filename", O_RDWR | O_CLOEXEC);
+ openat(0, "filename", O_RDWR | O_CLOEXEC);
--- /dev/null
+.. title:: clang-tidy - android-cloexec-socket
+
+android-cloexec-socket
+======================
+
+``socket()`` should include ``SOCK_CLOEXEC`` in its type argument to avoid the
+file descriptor leakage. Without this flag, an opened sensitive file would
+remain open across a fork+exec to a lower-privileged SELinux domain.
+
+Examples:
+
+.. code-block:: c++
+
+ socket(domain, type, SOCK_STREAM);
+
+ // becomes
+
+ socket(domain, type, SOCK_STREAM | SOCK_CLOEXEC);
--- /dev/null
+.. title:: clang-tidy - boost-use-to-string
+
+boost-use-to-string
+===================
+
+This check finds conversion from integer type like ``int`` to ``std::string`` or
+``std::wstring`` using ``boost::lexical_cast``, and replace it with calls to
+``std::to_string`` and ``std::to_wstring``.
+
+It doesn't replace conversion from floating points despite the ``to_string``
+overloads, because it would change the behaviour.
+
+
+ .. code-block:: c++
+
+ auto str = boost::lexical_cast<std::string>(42);
+ auto wstr = boost::lexical_cast<std::wstring>(2137LL);
+
+ // Will be changed to
+ auto str = std::to_string(42);
+ auto wstr = std::to_wstring(2137LL);
+
--- /dev/null
+.. title:: clang-tidy - bugprone-suspicious-memset-usage
+
+bugprone-suspicious-memset-usage
+================================
+
+This check finds ``memset()`` calls with potential mistakes in their arguments.
+Considering the function as ``void* memset(void* destination, int fill_value,
+size_t byte_count)``, the following cases are covered:
+
+**Case 1: Fill value is a character ``'0'``**
+
+Filling up a memory area with ASCII code 48 characters is not customary,
+possibly integer zeroes were intended instead.
+The check offers a replacement of ``'0'`` with ``0``. Memsetting character
+pointers with ``'0'`` is allowed.
+
+**Case 2: Fill value is truncated**
+
+Memset converts ``fill_value`` to ``unsigned char`` before using it. If
+``fill_value`` is out of unsigned character range, it gets truncated
+and memory will not contain the desired pattern.
+
+**Case 3: Byte count is zero**
+
+Calling memset with a literal zero in its ``byte_count`` argument is likely
+to be unintended and swapped with ``fill_value``. The check offers to swap
+these two arguments.
+
+Corresponding cpplint.py check name: ``runtime/memset``.
+
+
+Examples:
+
+.. code-block:: c++
+
+ void foo() {
+ int i[5] = {1, 2, 3, 4, 5};
+ int *ip = i;
+ char c = '1';
+ char *cp = &c;
+ int v = 0;
+
+ // Case 1
+ memset(ip, '0', 1); // suspicious
+ memset(cp, '0', 1); // OK
+
+ // Case 2
+ memset(ip, 0xabcd, 1); // fill value gets truncated
+ memset(ip, 0x00, 1); // OK
+
+ // Case 3
+ memset(ip, sizeof(int), v); // zero length, potentially swapped
+ memset(ip, 0, 1); // OK
+ }
--- /dev/null
+.. title:: clang-tidy - bugprone-undefined-memory-manipulation
+
+bugprone-undefined-memory-manipulation
+======================================
+
+Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior.
--- /dev/null
+.. title:: clang-tidy - cert-dcl03-c
+.. meta::
+ :http-equiv=refresh: 5;URL=misc-static-assert.html
+
+cert-dcl03-c
+============
+
+The cert-dcl03-c check is an alias, please see
+`misc-static-assert <misc-static-assert.html>`_ for more information.
--- /dev/null
+.. title:: clang-tidy - cert-dcl21-cpp
+
+cert-dcl21-cpp
+==============
+
+This check flags postfix ``operator++`` and ``operator--`` declarations
+if the return type is not a const object. This also warns if the return type
+is a reference type.
+
+This check corresponds to the CERT C++ Coding Standard recommendation
+`DCL21-CPP. Overloaded postfix increment and decrement operators should return a const object
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL21-CPP.+Overloaded+postfix+increment+and+decrement+operators+should+return+a+const+object>`_.
--- /dev/null
+.. title:: clang-tidy - cert-dcl50-cpp
+
+cert-dcl50-cpp
+==============
+
+This check flags all function definitions (but not declarations) of C-style
+variadic functions.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`DCL50-CPP. Do not define a C-style variadic function
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL50-CPP.+Do+not+define+a+C-style+variadic+function>`_.
--- /dev/null
+.. title:: clang-tidy - cert-dcl54-cpp
+.. meta::
+ :http-equiv=refresh: 5;URL=misc-new-delete-overloads.html
+
+cert-dcl54-cpp
+==============
+
+The cert-dcl54-cpp check is an alias, please see
+`misc-new-delete-overloads <misc-new-delete-overloads.html>`_ for more
+information.
--- /dev/null
+.. title:: clang-tidy - cert-dcl58-cpp
+
+cert-dcl58-cpp
+==============
+
+Modification of the ``std`` or ``posix`` namespace can result in undefined
+behavior.
+This check warns for such modifications.
+
+Examples:
+
+.. code-block:: c++
+
+ namespace std {
+ int x; // May cause undefined behavior.
+ }
+
+
+This check corresponds to the CERT C++ Coding Standard rule
+`DCL58-CPP. Do not modify the standard namespaces
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL58-CPP.+Do+not+modify+the+standard+namespaces>`_.
--- /dev/null
+.. title:: clang-tidy - cert-dcl59-cpp
+.. meta::
+ :http-equiv=refresh: 5;URL=google-build-namespaces.html
+
+cert-dcl59-cpp
+==============
+
+The cert-dcl59-cpp check is an alias, please see
+`google-build-namespaces <google-build-namespaces.html>`_ for more information.
--- /dev/null
+.. title:: clang-tidy - cert-env33-c
+
+cert-env33-c
+============
+
+This check flags calls to ``system()``, ``popen()``, and ``_popen()``, which
+execute a command processor. It does not flag calls to ``system()`` with a null
+pointer argument, as such a call checks for the presence of a command processor
+but does not actually attempt to execute a command.
+
+This check corresponds to the CERT C Coding Standard rule
+`ENV33-C. Do not call system()
+<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2130132>`_.
--- /dev/null
+.. title:: clang-tidy - cert-err09-cpp
+.. meta::
+ :http-equiv=refresh: 5;URL=misc-throw-by-value-catch-by-reference.html
+
+cert-err09-cpp
+==============
+
+The cert-err09-cpp check is an alias, please see
+`misc-throw-by-value-catch-by-reference <misc-throw-by-value-catch-by-reference.html>`_
+for more information.
--- /dev/null
+.. title:: clang-tidy - cert-err34-c
+
+cert-err34-c
+============
+
+This check flags calls to string-to-number conversion functions that do not
+verify the validity of the conversion, such as ``atoi()`` or ``scanf()``. It
+does not flag calls to ``strtol()``, or other, related conversion functions that
+do perform better error checking.
+
+.. code-block:: c
+
+ #include <stdlib.h>
+
+ void func(const char *buff) {
+ int si;
+
+ if (buff) {
+ si = atoi(buff); /* 'atoi' used to convert a string to an integer, but function will
+ not report conversion errors; consider using 'strtol' instead. */
+ } else {
+ /* Handle error */
+ }
+ }
+
+This check corresponds to the CERT C Coding Standard rule
+`ERR34-C. Detect errors when converting a string to a number
+<https://www.securecoding.cert.org/confluence/display/c/ERR34-C.+Detect+errors+when+converting+a+string+to+a+number>`_.
--- /dev/null
+.. title:: clang-tidy - cert-err52-cpp
+
+cert-err52-cpp
+==============
+
+This check flags all call expressions involving ``setjmp()`` and ``longjmp()``.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR52-CPP. Do not use setjmp() or longjmp()
+<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=1834>`_.
--- /dev/null
+.. title:: clang-tidy - cert-err58-cpp
+
+cert-err58-cpp
+==============
+
+This check flags all ``static`` or ``thread_local`` variable declarations where
+the initializer for the object may throw an exception.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR58-CPP. Handle all exceptions thrown before main() begins executing
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR58-CPP.+Handle+all+exceptions+thrown+before+main%28%29+begins+executing>`_.
--- /dev/null
+.. title:: clang-tidy - cert-err60-cpp
+
+cert-err60-cpp
+==============
+
+This check flags all throw expressions where the exception object is not nothrow
+copy constructible.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR60-CPP. Exception objects must be nothrow copy constructible
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible>`_.
--- /dev/null
+.. title:: clang-tidy - cert-err61-cpp
+.. meta::
+ :http-equiv=refresh: 5;URL=misc-throw-by-value-catch-by-reference.html
+
+cert-err61-cpp
+==============
+
+The cert-err61-cpp check is an alias, please see
+`misc-throw-by-value-catch-by-reference <misc-throw-by-value-catch-by-reference.html>`_
+for more information.
--- /dev/null
+.. title:: clang-tidy - cert-fio38-c
+.. meta::
+ :http-equiv=refresh: 5;URL=misc-non-copyable-objects.html
+
+cert-fio38-c
+============
+
+The cert-fio38-c check is an alias, please see
+`misc-non-copyable-objects <misc-non-copyable-objects.html>`_ for more
+information.
--- /dev/null
+.. title:: clang-tidy - cert-flp30-c
+
+cert-flp30-c
+============
+
+This check flags ``for`` loops where the induction expression has a
+floating-point type.
+
+This check corresponds to the CERT C Coding Standard rule
+`FLP30-C. Do not use floating-point variables as loop counters
+<https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters>`_.
--- /dev/null
+.. title:: clang-tidy - cert-msc30-c
+.. meta::
+ :http-equiv=refresh: 5;URL=cert-msc50-cpp.html
+
+cert-msc30-c
+============
+
+The cert-msc30-c check is an alias, please see
+`cert-msc50-cpp <cert-msc50-cpp.html>`_ for more information.
--- /dev/null
+.. title:: clang-tidy - cert-msc50-cpp
+
+cert-msc50-cpp
+==============
+
+Pseudorandom number generators use mathematical algorithms to produce a sequence
+of numbers with good statistical properties, but the numbers produced are not
+genuinely random. The ``std::rand()`` function takes a seed (number), runs a
+mathematical operation on it and returns the result. By manipulating the seed
+the result can be predictable. This check warns for the usage of
+``std::rand()``.
--- /dev/null
+.. title:: clang-tidy - cert-oop11-cpp\r
+.. meta::\r
+ :http-equiv=refresh: 5;URL=misc-move-constructor-init.html\r
+\r
+cert-oop11-cpp\r
+==============\r
+\r
+The cert-oop11-cpp check is an alias, please see\r
+`misc-move-constructor-init <misc-move-constructor-init.html>`_ for more\r
+information.\r
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-interfaces-global-init
+
+cppcoreguidelines-interfaces-global-init
+========================================
+
+This check flags initializers of globals that access extern objects,
+and therefore can lead to order-of-initialization problems.
+
+This rule is part of the "Interfaces" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-global-init
+
+Note that currently this does not flag calls to non-constexpr functions, and
+therefore globals could still be accessed from functions themselves.
+
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-no-malloc
+
+cppcoreguidelines-no-malloc
+===========================
+
+This check handles C-Style memory management using ``malloc()``, ``realloc()``,
+``calloc()`` and ``free()``. It warns about its use and tries to suggest the use
+of an appropriate RAII object.
+Furthermore, it can be configured to check against a user-specified list of functions
+that are used for memory management (e.g. ``posix_memalign()``).
+See `C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-mallocfree>`_.
+
+There is no attempt made to provide fix-it hints, since manual resource
+management isn't easily transformed automatically into RAII.
+
+.. code-block:: c++
+
+ // Warns each of the following lines.
+ // Containers like std::vector or std::string should be used.
+ char* some_string = (char*) malloc(sizeof(char) * 20);
+ char* some_string = (char*) realloc(sizeof(char) * 30);
+ free(some_string);
+
+ int* int_array = (int*) calloc(30, sizeof(int));
+
+ // Rather use a smartpointer or stack variable.
+ struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct));
+
+Options
+-------
+
+.. option:: Allocations
+
+ Semicolon-separated list of fully qualified names of memory allocation functions.
+ Defaults to ``::malloc;::calloc``.
+
+.. option:: Deallocations
+
+ Semicolon-separated list of fully qualified names of memory allocation functions.
+ Defaults to ``::free``.
+
+.. option:: Reallocations
+
+ Semicolon-separated list of fully qualified names of memory allocation functions.
+ Defaults to ``::realloc``.
+
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-array-to-pointer-decay
+
+cppcoreguidelines-pro-bounds-array-to-pointer-decay
+===================================================
+
+This check flags all array to pointer decays.
+
+Pointers should not be used as arrays. ``span<T>`` is a bounds-checked, safe
+alternative to using pointers to access arrays.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-decay.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-constant-array-index
+
+cppcoreguidelines-pro-bounds-constant-array-index
+=================================================
+
+This check flags all array subscript expressions on static arrays and
+``std::arrays`` that either do not have a constant integer expression index or
+are out of bounds (for ``std::array``). For out-of-bounds checking of static
+arrays, see the `-Warray-bounds` Clang diagnostic.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-arrayindex.
+
+Options
+-------
+
+.. option:: GslHeader
+
+ The check can generate fixes after this option has been set to the name of
+ the include file that contains ``gsl::at()``, e.g. `"gsl/gsl.h"`.
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-pointer-arithmetic
+
+cppcoreguidelines-pro-bounds-pointer-arithmetic
+===============================================
+
+This check flags all usage of pointer arithmetic, because it could lead to an
+invalid pointer. Subtraction of two pointers is not flagged by this check.
+
+Pointers should only refer to single objects, and pointer arithmetic is fragile
+and easy to get wrong. ``span<T>`` is a bounds-checked, safe type for accessing
+arrays of data.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-arithmetic.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-const-cast
+
+cppcoreguidelines-pro-type-const-cast
+=====================================
+
+This check flags all uses of ``const_cast`` in C++ code.
+
+Modifying a variable that was declared const is undefined behavior, even with
+``const_cast``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-constcast.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-cstyle-cast
+
+cppcoreguidelines-pro-type-cstyle-cast
+======================================
+
+This check flags all use of C-style casts that perform a ``static_cast``
+downcast, ``const_cast``, or ``reinterpret_cast``.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type X to be accessed as if it were of an unrelated
+type Z. Note that a C-style ``(T)expression`` cast means to perform the first of
+the following that is possible: a ``const_cast``, a ``static_cast``, a
+``static_cast`` followed by a ``const_cast``, a ``reinterpret_cast``, or a
+``reinterpret_cast`` followed by a ``const_cast``. This rule bans
+``(T)expression`` only when used to perform an unsafe cast.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-cstylecast.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-member-init
+
+cppcoreguidelines-pro-type-member-init
+======================================
+
+The check flags user-defined constructor definitions that do not
+initialize all fields that would be left in an undefined state by
+default construction, e.g. builtins, pointers and record types without
+user-provided default constructors containing at least one such
+type. If these fields aren't initialized, the constructor will leave
+some of the memory in an undefined state.
+
+For C++11 it suggests fixes to add in-class field initializers. For
+older versions it inserts the field initializers into the constructor
+initializer list. It will also initialize any direct base classes that
+need to be zeroed in the constructor initializer list.
+
+The check takes assignment of fields in the constructor body into
+account but generates false positives for fields initialized in
+methods invoked in the constructor body.
+
+The check also flags variables with automatic storage duration that have record
+types without a user-provided constructor and are not initialized. The suggested
+fix is to zero initialize the variable via ``{}`` for C++11 and beyond or ``=
+{}`` for older language versions.
+
+Options
+-------
+
+.. option:: IgnoreArrays
+
+ If set to non-zero, the check will not warn about array members that are not
+ zero-initialized during construction. For performance critical code, it may
+ be important to not initialize fixed-size array members. Default is `0`.
+
+This rule is part of the "Type safety" profile of the C++ Core
+Guidelines, corresponding to rule Type.6. See
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-memberinit.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-reinterpret-cast
+
+cppcoreguidelines-pro-type-reinterpret-cast
+===========================================
+
+This check flags all uses of ``reinterpret_cast`` in C++ code.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type ``X`` to be accessed as if it were of an
+unrelated type ``Z``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-reinterpretcast.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-static-cast-downcast
+
+cppcoreguidelines-pro-type-static-cast-downcast
+===============================================
+
+This check flags all usages of ``static_cast``, where a base class is casted to
+a derived class. In those cases, a fix-it is provided to convert the cast to a
+``dynamic_cast``.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type ``X`` to be accessed as if it were of an
+unrelated type ``Z``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-downcast.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-union-access
+
+cppcoreguidelines-pro-type-union-access
+=======================================
+
+This check flags all access to members of unions. Passing unions as a whole is
+not flagged.
+
+Reading from a union member assumes that member was the last one written, and
+writing to a union member assumes another member with a nontrivial destructor
+had its destructor called. This is fragile because it cannot generally be
+enforced to be safe in the language and so relies on programmer discipline to
+get it right.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-unions.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-pro-type-vararg
+
+cppcoreguidelines-pro-type-vararg
+=================================
+
+This check flags all calls to c-style vararg functions and all use of
+``va_arg``.
+
+To allow for SFINAE use of vararg functions, a call is not flagged if a literal
+0 is passed as the only vararg argument.
+
+Passing to varargs assumes the correct type will be read. This is fragile
+because it cannot generally be enforced to be safe in the language and so relies
+on programmer discipline to get it right.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-varargs.
--- /dev/null
+.. title:: clang-tidy - cppcoreguidelines-slicing
+
+cppcoreguidelines-slicing
+=========================
+
+Flags slicing of member variables or vtable. Slicing happens when copying a
+derived object into a base object: the members of the derived object (both
+member variables and virtual member functions) will be discarded. This can be
+misleading especially for member function slicing, for example:
+
+.. code-block:: 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.
+
+See the relevant C++ Core Guidelines sections for details:
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es63-dont-slice
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c145-access-polymorphic-objects-through-pointers-and-references
--- /dev/null
+.. 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
+suppressed by explicit user-definitions. The relationship between which
+functions will be suppressed 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.
+
+Options
+-------
+
+.. option:: AllowSoleDefaultDtor
+
+ When set to `1` (default is `0`), this check doesn't flag classes with a sole, explicitly
+ defaulted destructor. An example for such a class is:
+
+ .. code-block:: c++
+
+ struct A {
+ virtual ~A() = default;
+ };
+
+.. option:: AllowMissingMoveFunctions
+
+ When set to `1` (default is `0`), this check doesn't flag classes which define no move
+ operations at all. It still flags classes which define only one of either
+ move constructor or move assignment operator. With this option enabled, the following class won't be flagged:
+
+ .. code-block:: c++
+
+ struct A {
+ A(const A&);
+ A& operator=(const A&);
+ ~A();
+ }
--- /dev/null
+.. title:: clang-tidy - google-build-explicit-make-pair
+
+google-build-explicit-make-pair
+===============================
+
+Check that ``make_pair``'s template arguments are deduced.
+
+G++ 4.6 in C++11 mode fails badly if ``make_pair``'s template arguments are
+specified explicitly, and such use isn't intended in any case.
+
+Corresponding cpplint.py check name: `build/explicit_make_pair`.
--- /dev/null
+.. title:: clang-tidy - google-build-namespaces
+
+google-build-namespaces
+=======================
+
+`cert-dcl59-cpp` redirects here as an alias for this check.
+
+Finds anonymous namespaces in headers.
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
+
+Corresponding cpplint.py check name: `build/namespaces`.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+ A comma-separated list of filename extensions of header files (the filename
+ extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+ For header files without an extension, use an empty string (if there are no
+ other desired extensions) or leave an empty element in the list. e.g.,
+ "h,hh,hpp,hxx," (note the trailing comma).
--- /dev/null
+.. title:: clang-tidy - google-build-using-namespace
+
+google-build-using-namespace
+============================
+
+Finds ``using namespace`` directives.
+
+The check implements the following rule of the
+`Google C++ Style Guide <https://google.github.io/styleguide/cppguide.html#Namespaces>`_:
+
+ You may not use a using-directive to make all names from a namespace
+ available.
+
+ .. code-block:: c++
+
+ // Forbidden -- This pollutes the namespace.
+ using namespace foo;
+
+Corresponding cpplint.py check name: `build/namespaces`.
--- /dev/null
+.. title:: clang-tidy - google-default-arguments
+
+google-default-arguments
+========================
+
+Checks that default arguments are not given for virtual methods.
+
+See https://google.github.io/styleguide/cppguide.html#Default_Arguments
--- /dev/null
+.. title:: clang-tidy - google-explicit-constructor
+
+google-explicit-constructor
+===========================
+
+
+Checks that constructors callable with a single argument and conversion
+operators are marked explicit to avoid the risk of unintentional implicit
+conversions.
+
+Consider this example:
+
+.. code-block:: c++
+
+ struct S {
+ int x;
+ operator bool() const { return true; }
+ };
+
+ bool f() {
+ S a{1};
+ S b{2};
+ return a == b;
+ }
+
+The function will return ``true``, since the objects are implicitly converted to
+``bool`` before comparison, which is unlikely to be the intent.
+
+The check will suggest inserting ``explicit`` before the constructor or
+conversion operator declaration. However, copy and move constructors should not
+be explicit, as well as constructors taking a single ``initializer_list``
+argument.
+
+This code:
+
+.. code-block:: c++
+
+ struct S {
+ S(int a);
+ explicit S(const S&);
+ operator bool() const;
+ ...
+
+will become
+
+.. code-block:: c++
+
+ struct S {
+ explicit S(int a);
+ S(const S&);
+ explicit operator bool() const;
+ ...
+
+
+
+See https://google.github.io/styleguide/cppguide.html#Explicit_Constructors
--- /dev/null
+.. title:: clang-tidy - google-global-names-in-headers
+
+google-global-names-in-headers
+==============================
+
+Flag global namespace pollution in header files. Right now it only triggers on
+``using`` declarations and directives.
+
+The relevant style guide section is
+https://google.github.io/styleguide/cppguide.html#Namespaces.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+ A comma-separated list of filename extensions of header files (the filename
+ extensions should not contain "." prefix). Default is "h".
+ For header files without an extension, use an empty string (if there are no
+ other desired extensions) or leave an empty element in the list. e.g.,
+ "h,hh,hpp,hxx," (note the trailing comma).
--- /dev/null
+.. title:: clang-tidy - google-readability-braces-around-statements
+.. meta::
+ :http-equiv=refresh: 5;URL=readability-braces-around-statements.html
+
+google-readability-braces-around-statements
+===========================================
+
+The google-readability-braces-around-statements check is an alias, please see
+`readability-braces-around-statements <readability-braces-around-statements.html>`_
+for more information.
--- /dev/null
+.. title:: clang-tidy - google-readability-casting
+
+google-readability-casting
+==========================
+
+Finds usages of C-style casts.
+
+https://google.github.io/styleguide/cppguide.html#Casting
+
+Corresponding cpplint.py check name: `readability/casting`.
+
+This check is similar to `-Wold-style-cast`, but it suggests automated fixes
+in some cases. The reported locations should not be different from the
+ones generated by `-Wold-style-cast`.
--- /dev/null
+.. title:: clang-tidy - google-readability-function-size
+.. meta::
+ :http-equiv=refresh: 5;URL=readability-function-size.html
+
+google-readability-function-size
+================================
+
+The google-readability-function-size check is an alias, please see
+`readability-function-size <readability-function-size.html>`_ for more
+information.
--- /dev/null
+.. title:: clang-tidy - google-readability-namespace-comments
+.. meta::
+ :http-equiv=refresh: 5;URL=llvm-namespace-comment.html
+
+google-readability-namespace-comments
+=====================================
+
+The google-readability-namespace-comments check is an alias, please see
+`llvm-namespace-comment <llvm-namespace-comment.html>`_ for more information.
--- /dev/null
+.. title:: clang-tidy - google-readability-redundant-smartptr-get
+.. meta::
+ :http-equiv=refresh: 5;URL=readability-redundant-smartptr-get.html
+
+google-readability-redundant-smartptr-get
+=========================================
+
+The google-readability-redundant-smartptr-get check is an alias, please see
+`readability-redundant-smartptr-get <readability-redundant-smartptr-get.html>`_
+for more information.
--- /dev/null
+.. title:: clang-tidy - google-readability-todo
+
+google-readability-todo
+=======================
+
+Finds TODO comments without a username or bug number.
+
+The relevant style guide section is
+https://google.github.io/styleguide/cppguide.html#TODO_Comments.
+
+Corresponding cpplint.py check: `readability/todo`
--- /dev/null
+.. title:: clang-tidy - google-runtime-int
+
+google-runtime-int
+==================
+
+Finds uses of ``short``, ``long`` and ``long long`` and suggest replacing them
+with ``u?intXX(_t)?``.
+
+The corresponding style guide rule:
+https://google.github.io/styleguide/cppguide.html#Integer_Types.
+
+Correspondig cpplint.py check: `runtime/int`.
+
+Options
+-------
+
+.. option:: UnsignedTypePrefix
+
+ A string specifying the unsigned type prefix. Default is `uint`.
+
+.. option:: SignedTypePrefix
+
+ A string specifying the signed type prefix. Default is `int`.
+
+.. option:: TypeSuffix
+
+ A string specifying the type suffix. Default is an empty string.
--- /dev/null
+.. title:: clang-tidy - google-runtime-member-string-references
+
+google-runtime-member-string-references
+=======================================
+
+Finds members of type ``const string&``.
+
+const string reference members are generally considered unsafe as they can be
+created from a temporary quite easily.
+
+.. code-block:: c++
+
+ struct S {
+ S(const string &Str) : Str(Str) {}
+ const string &Str;
+ };
+ S instance("string");
+
+In the constructor call a string temporary is created from ``const char *`` and
+destroyed immediately after the call. This leaves around a dangling reference.
+
+This check emit warnings for both ``std::string`` and ``::string`` const
+reference members.
+
+Corresponding cpplint.py check name: `runtime/member_string_reference`.
--- /dev/null
+.. title:: clang-tidy - google-runtime-operator
+
+google-runtime-operator
+=======================
+
+Finds overloads of unary ``operator &``.
+
+https://google.github.io/styleguide/cppguide.html#Operator_Overloading
+
+Corresponding cpplint.py check name: `runtime/operator`.
--- /dev/null
+.. title:: clang-tidy - google-runtime-references
+
+google-runtime-references
+=========================
+
+Checks the usage of non-constant references in function parameters.
+
+The corresponding style guide rule:
+https://google.github.io/styleguide/cppguide.html#Reference_Arguments
+
+
+Options
+-------
+
+.. option:: WhiteListTypes
+
+ A semicolon-separated list of names of whitelist types. Default is empty.
--- /dev/null
+.. title:: clang-tidy - hicpp-explicit-conversions
+
+hicpp-explicit-conversions
+==========================
+
+This check is an alias for `google-explicit-constructor <google-explicit-constructor.hml>`_.
+Used to enforce parts of `rule 5.4.1 <http://www.codingstandard.com/rule/5-4-1-only-use-casting-forms-static_cast-excl-void-dynamic_cast-or-explicit-constructor-call/>`_.
+This check will enforce that constructors and conversion operators are marked `explicit`.
+Other forms of casting checks are implemented in other places.
+The following checks can be used to check for more forms of casting:
+
+- `cppcoreguidelines-pro-type-static-cast-downcast <cppcoreguidelines-pro-type-static-cast-downcast.html>`_
+- `cppcoreguidelines-pro-type-reinterpret-cast <cppcoreguidelines-pro-type-reinterpret-cast.html>`_
+- `cppcoreguidelines-pro-type-const-cast <cppcoreguidelines-pro-type-const-cast.html>`_
+- `cppcoreguidelines-pro-type-cstyle-cast <cppcoreguidelines-pro-type-cstyle-cast.html>`_
--- /dev/null
+.. title:: clang-tidy - hicpp-function-size
+
+hicpp-function-size
+===================
+
+This check is an alias for `readability-function-size <readability-function-size.hml>`_.
+Useful to enforce multiple sections on function complexity.
+
+- `rule 8.2.2 <http://www.codingstandard.com/rule/8-2-2-do-not-declare-functions-with-an-excessive-number-of-parameters/>`_
+- `rule 8.3.1 <http://www.codingstandard.com/rule/8-3-1-do-not-write-functions-with-an-excessive-mccabe-cyclomatic-complexity/>`_
+- `rule 8.3.2 <http://www.codingstandard.com/rule/8-3-2-do-not-write-functions-with-a-high-static-program-path-count/>`_
--- /dev/null
+.. title:: clang-tidy - hicpp-invalid-access-moved
+
+hicpp-invalid-access-moved
+==========================
+
+This check is an alias for `misc-use-after-move <misc-use-after-move.hml>`_.
+
+Implements parts of the `rule 8.4.1 <http://www.codingstandard.com/rule/8-4-1-do-not-access-an-invalid-object-or-an-object-with-indeterminate-value/>`_ to check if moved-from objects are accessed.
--- /dev/null
+.. title:: clang-tidy - hicpp-member-init
+
+hicpp-member-init
+=================
+
+This check is an alias for `cppcoreguidelines-pro-type-member-init <cppcoreguidelines-pro-type-member-init.hml>`_.
+Implements the check for
+`rule 12.4.2 <http://www.codingstandard.com/rule/12-4-2-ensure-that-a-constructor-initializes-explicitly-all-base-classes-and-non-static-data-members/>`_
+to initialize class members in the right order.
--- /dev/null
+.. title:: clang-tidy - hicpp-named-parameter
+
+hicpp-named-parameter
+=====================
+
+This check is an alias for `readability-named-parameter <readability-named-parameter.hml>`_.
+
+Implements `rule 8.2.1 <http://www.codingstandard.com/rule/8-2-1-make-parameter-names-absent-or-identical-in-all-declarations/>`_.
--- /dev/null
+.. title:: clang-tidy - hicpp-new-delete-operators
+
+hicpp-new-delete-operators
+==========================
+
+This check is an alias for `misc-new-delete-overloads <misc-new-delete-overloads.hml>`_.
+Implements `rule 12.3.1 <http://www.codingstandard.com/section/12-3-free-store/>`_ to ensure
+the `new` and `delete` operators have the correct signature.
--- /dev/null
+.. title:: clang-tidy - hicpp-no-assembler
+
+hicpp-no-assembler
+===================
+
+Check for assembler statements. No fix is offered.
+
+Inline assembler is forbidden by the `High Intergrity C++ Coding Standard
+<http://www.codingstandard.com/section/7-5-the-asm-declaration/>`_
+as it restricts the portability of code.
--- /dev/null
+.. title:: clang-tidy - hicpp-noexcept-move
+
+hicpp-noexcept-move
+===================
+
+This check is an alias for `misc-noexcept-moveconstructor <misc-noexcept-moveconstructor.hml>`_.
+Checks `rule 12.5.4 <http://www.codingstandard.com/rule/12-5-4-declare-noexcept-the-move-constructor-and-move-assignment-operator>`_ to mark move assignment and move construction `noexcept`.
--- /dev/null
+.. title:: clang-tidy - hicpp-special-member-functions
+
+hicpp-special-member-functions
+==============================
+
+This check is an alias for `cppcoreguidelines-special-member-functions <cppcoreguidelines-special-member-functions.hml>`_.
+Checks that special member functions have the correct signature, according to `rule 12.5.7 <http://www.codingstandard.com/rule/12-5-7-declare-assignment-operators-with-the-ref-qualifier/>`_.
--- /dev/null
+.. title:: clang-tidy - hicpp-undelegated-construtor
+
+hicpp-undelegated-constructor
+=============================
+
+This check is an alias for `misc-undelegated-constructor <misc-undelegated-constructor.hml>`_.
+Partially implements `rule 12.4.5 <http://www.codingstandard.com/rule/12-4-5-use-delegating-constructors-to-reduce-code-duplication/>`_
+to find misplaced constructor calls inside a constructor.
+
+.. code-block:: c++
+
+ struct Ctor {
+ Ctor();
+ Ctor(int);
+ Ctor(int, int);
+ Ctor(Ctor *i) {
+ // All Ctor() calls result in a temporary object
+ Ctor(); // did you intend to call a delegated constructor?
+ Ctor(0); // did you intend to call a delegated constructor?
+ Ctor(1, 2); // did you intend to call a delegated constructor?
+ foo();
+ }
+ };
--- /dev/null
+.. title:: clang-tidy - hicpp-use-equals-defaults
+
+hicpp-use-equals-default
+========================
+
+This check is an alias for `modernize-use-equals-default <modernize-use-equals-default.hml>`_.
+Implements `rule 12.5.1 <http://www.codingstandard.com/rule/12-5-1-define-explicitly-default-or-delete-implicit-special-member-functions-of-concrete-classes/>`_ to explicitly default special member functions.
--- /dev/null
+.. title:: clang-tidy - hicpp-use-equals-delete
+
+hicpp-use-equals-delete
+=======================
+
+This check is an alias for `modernize-use-equals-delete <modernize-use-equals-delete.hml>`_.
+Implements `rule 12.5.1 <http://www.codingstandard.com/rule/12-5-1-define-explicitly-default-or-delete-implicit-special-member-functions-of-concrete-classes/>`_
+to explicitly default or delete special member functions.
--- /dev/null
+.. title:: clang-tidy - hicpp-use-override
+
+hicpp-use-override
+==================
+
+This check is an alias for `modernize-use-override <modernize-use-override.hml>`_.
+Implements `rule 10.2.1 <http://www.codingstandard.com/section/10-2-virtual-functions/>`_ to
+declare a virtual function `override` when overriding.
--- /dev/null
+.. title:: clang-tidy - Clang-Tidy Checks
+
+Clang-Tidy Checks
+=================
+
+.. toctree::
+ android-cloexec-creat
+ android-cloexec-fopen
+ android-cloexec-open
+ android-cloexec-socket
+ boost-use-to-string
+ bugprone-suspicious-memset-usage
+ bugprone-undefined-memory-manipulation
+ cert-dcl03-c (redirects to misc-static-assert) <cert-dcl03-c>
+ cert-dcl21-cpp
+ cert-dcl50-cpp
+ cert-dcl54-cpp (redirects to misc-new-delete-overloads) <cert-dcl54-cpp>
+ cert-dcl58-cpp
+ cert-dcl59-cpp (redirects to google-build-namespaces) <cert-dcl59-cpp>
+ cert-env33-c
+ cert-err09-cpp (redirects to misc-throw-by-value-catch-by-reference) <cert-err09-cpp>
+ cert-err34-c
+ cert-err52-cpp
+ cert-err58-cpp
+ cert-err60-cpp
+ cert-err61-cpp (redirects to misc-throw-by-value-catch-by-reference) <cert-err61-cpp>
+ cert-fio38-c (redirects to misc-non-copyable-objects) <cert-fio38-c>
+ cert-flp30-c
+ cert-msc30-c (redirects to cert-msc50-cpp) <cert-msc30-c>
+ cert-msc50-cpp
+ cert-oop11-cpp (redirects to misc-move-constructor-init) <cert-oop11-cpp>
+ cppcoreguidelines-interfaces-global-init
+ cppcoreguidelines-no-malloc
+ cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ cppcoreguidelines-pro-bounds-constant-array-index
+ cppcoreguidelines-pro-bounds-pointer-arithmetic
+ cppcoreguidelines-pro-type-const-cast
+ cppcoreguidelines-pro-type-cstyle-cast
+ cppcoreguidelines-pro-type-member-init
+ cppcoreguidelines-pro-type-reinterpret-cast
+ cppcoreguidelines-pro-type-static-cast-downcast
+ 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
+ google-default-arguments
+ google-explicit-constructor
+ google-global-names-in-headers
+ google-readability-braces-around-statements (redirects to readability-braces-around-statements) <google-readability-braces-around-statements>
+ google-readability-casting
+ google-readability-function-size (redirects to readability-function-size) <google-readability-function-size>
+ google-readability-namespace-comments (redirects to llvm-namespace-comment) <google-readability-namespace-comments>
+ google-readability-redundant-smartptr-get (redirects to readability-redundant-smartptr-get) <google-readability-redundant-smartptr-get>
+ google-readability-todo
+ google-runtime-int
+ google-runtime-member-string-references
+ google-runtime-operator
+ google-runtime-references
+ hicpp-explicit-conversions
+ hicpp-function-size
+ hicpp-invalid-access-moved
+ hicpp-member-init
+ hicpp-named-parameter
+ hicpp-new-delete-operators
+ hicpp-no-assembler
+ hicpp-noexcept-move
+ hicpp-special-member-functions
+ hicpp-undelegated-constructor
+ hicpp-use-equals-default
+ hicpp-use-equals-delete
+ hicpp-use-override
+ llvm-header-guard
+ llvm-include-order
+ llvm-namespace-comment
+ llvm-twine-local
+ misc-argument-comment
+ misc-assert-side-effect
+ misc-bool-pointer-implicit-conversion
+ misc-dangling-handle
+ misc-definitions-in-headers
+ misc-fold-init-type
+ misc-forward-declaration-namespace
+ misc-forwarding-reference-overload
+ misc-inaccurate-erase
+ misc-incorrect-roundings
+ misc-inefficient-algorithm
+ misc-lambda-function-name
+ misc-macro-parentheses
+ misc-macro-repeated-side-effects
+ misc-misplaced-const
+ misc-misplaced-widening-cast
+ misc-move-const-arg
+ misc-move-constructor-init
+ misc-move-forwarding-reference
+ misc-multiple-statement-macro
+ misc-new-delete-overloads
+ misc-noexcept-move-constructor
+ misc-non-copyable-objects
+ misc-redundant-expression
+ misc-sizeof-container
+ misc-sizeof-expression
+ misc-static-assert
+ misc-string-compare
+ misc-string-constructor
+ misc-string-integer-assignment
+ misc-string-literal-with-embedded-nul
+ misc-suspicious-enum-usage
+ misc-suspicious-missing-comma
+ misc-suspicious-semicolon
+ misc-suspicious-string-compare
+ misc-swapped-arguments
+ misc-throw-by-value-catch-by-reference
+ misc-unconventional-assign-operator
+ misc-undelegated-constructor
+ misc-uniqueptr-reset-release
+ misc-unused-alias-decls
+ misc-unused-parameters
+ misc-unused-raii
+ misc-unused-using-decls
+ misc-use-after-move
+ misc-virtual-near-miss
+ modernize-avoid-bind
+ modernize-deprecated-headers
+ modernize-loop-convert
+ modernize-make-shared
+ modernize-make-unique
+ modernize-pass-by-value
+ modernize-raw-string-literal
+ modernize-redundant-void-arg
+ modernize-replace-auto-ptr
+ modernize-replace-random-shuffle
+ modernize-return-braced-init-list
+ modernize-shrink-to-fit
+ modernize-unary-static-assert
+ modernize-use-auto
+ modernize-use-bool-literals
+ modernize-use-default-member-init
+ modernize-use-emplace
+ modernize-use-equals-default
+ modernize-use-equals-delete
+ modernize-use-noexcept
+ modernize-use-nullptr
+ modernize-use-override
+ modernize-use-transparent-functors
+ modernize-use-using
+ mpi-buffer-deref
+ mpi-type-mismatch
+ performance-faster-string-find
+ performance-for-range-copy
+ performance-implicit-cast-in-loop
+ performance-inefficient-string-concatenation
+ performance-inefficient-vector-operation
+ performance-type-promotion-in-math-fn
+ performance-unnecessary-copy-initialization
+ performance-unnecessary-value-param
+ readability-avoid-const-params-in-decls
+ readability-braces-around-statements
+ readability-container-size-empty
+ readability-delete-null-pointer
+ readability-deleted-default
+ readability-else-after-return
+ readability-function-size
+ readability-identifier-naming
+ readability-implicit-bool-cast
+ readability-inconsistent-declaration-parameter-name
+ readability-misleading-indentation
+ readability-misplaced-array-index
+ readability-named-parameter
+ readability-non-const-parameter
+ readability-redundant-control-flow
+ readability-redundant-declaration
+ readability-redundant-function-ptr-dereference
+ readability-redundant-member-init
+ readability-redundant-smartptr-get
+ readability-redundant-string-cstr
+ readability-redundant-string-init
+ readability-simplify-boolean-expr
+ readability-static-definition-in-anonymous-namespace
+ readability-uniqueptr-delete-release
--- /dev/null
+.. title:: clang-tidy - llvm-header-guard
+
+llvm-header-guard
+=================
+
+Finds and fixes header guards that do not adhere to LLVM style.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+ A comma-separated list of filename extensions of header files (the filename
+ extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+ For header files without an extension, use an empty string (if there are no
+ other desired extensions) or leave an empty element in the list. e.g.,
+ "h,hh,hpp,hxx," (note the trailing comma).
--- /dev/null
+.. title:: clang-tidy - llvm-include-order
+
+llvm-include-order
+==================
+
+
+Checks the correct order of ``#includes``.
+
+See http://llvm.org/docs/CodingStandards.html#include-style
--- /dev/null
+.. title:: clang-tidy - llvm-namespace-comment
+
+llvm-namespace-comment
+======================
+
+`google-readability-namespace-comments` redirects here as an alias for this
+check.
+
+Checks that long namespaces have a closing comment.
+
+http://llvm.org/docs/CodingStandards.html#namespace-indentation
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
+
+.. code-block:: c++
+
+ namespace n1 {
+ void f();
+ }
+
+ // becomes
+
+ namespace n1 {
+ void f();
+ } // namespace n1
+
+
+Options
+-------
+
+.. option:: ShortNamespaceLines
+
+ Requires the closing brace of the namespace definition to be followed by a
+ closing comment if the body of the namespace has more than
+ `ShortNamespaceLines` lines of code. The value is an unsigned integer that
+ defaults to `1U`.
+
+.. option:: SpacesBeforeComments
+
+ An unsigned integer specifying the number of spaces before the comment
+ closing a namespace definition. Default is `1U`.
--- /dev/null
+.. title:: clang-tidy - llvm-twine-local
+
+llvm-twine-local
+================
+
+
+Looks for local ``Twine`` variables which are prone to use after frees and
+should be generally avoided.
+
+.. code-block:: c++
+
+ static Twine Moo = Twine("bark") + "bah";
+
+ // becomes
+
+ static std::string Moo = (Twine("bark") + "bah").str();
--- /dev/null
+.. title:: clang-tidy - misc-argument-comment
+
+misc-argument-comment
+=====================
+
+Checks that argument comments match parameter names.
+
+The check understands argument comments in the form ``/*parameter_name=*/``
+that are placed right before the argument.
+
+.. code-block:: c++
+
+ void f(bool foo);
+
+ ...
+
+ f(/*bar=*/true);
+ // warning: argument name 'bar' in comment does not match parameter name 'foo'
+
+The check tries to detect typos and suggest automated fixes for them.
+
+Options
+-------
+
+.. option:: StrictMode
+
+ When zero (default value), the check will ignore leading and trailing
+ underscores and case when comparing names -- otherwise they are taken into
+ account.
--- /dev/null
+.. title:: clang-tidy - misc-assert-side-effect
+
+misc-assert-side-effect
+=======================
+
+Finds ``assert()`` with side effect.
+
+The condition of ``assert()`` is evaluated only in debug builds so a
+condition with side effect can cause different behavior in debug / release
+builds.
+
+Options
+-------
+
+.. option:: AssertMacros
+
+ A comma-separated list of the names of assert macros to be checked.
+
+.. option:: CheckFunctionCalls
+
+ Whether to treat non-const member and non-member functions as they produce
+ side effects. Disabled by default because it can increase the number of false
+ positive warnings.
--- /dev/null
+.. title:: clang-tidy - misc-bool-pointer-implicit-conversion
+
+misc-bool-pointer-implicit-conversion
+=====================================
+
+Checks for conditions based on implicit conversion from a ``bool`` pointer to
+``bool``.
+
+Example:
+
+.. code-block:: c++
+
+ bool *p;
+ if (p) {
+ // Never used in a pointer-specific way.
+ }
--- /dev/null
+.. title:: clang-tidy - misc-dangling-handle
+
+misc-dangling-handle
+====================
+
+Detect dangling references in value handles like
+``std::experimental::string_view``.
+These dangling references can be a result of constructing handles from temporary
+values, where the temporary is destroyed soon after the handle is created.
+
+Examples:
+
+.. code-block:: c++
+
+ string_view View = string(); // View will dangle.
+ string A;
+ View = A + "A"; // still dangle.
+
+ vector<string_view> V;
+ V.push_back(string()); // V[0] is dangling.
+ V.resize(3, string()); // V[1] and V[2] will also dangle.
+
+ string_view f() {
+ // All these return values will dangle.
+ return string();
+ string S;
+ return S;
+ char Array[10]{};
+ return Array;
+ }
+
+Options
+-------
+
+.. option:: HandleClasses
+
+ A semicolon-separated list of class names that should be treated as handles.
+ By default only ``std::experimental::basic_string_view`` is considered.
--- /dev/null
+.. title:: clang-tidy - misc-definitions-in-headers
+
+misc-definitions-in-headers
+===========================
+
+Finds non-extern non-inline function and variable definitions in header files,
+which can lead to potential ODR violations in case these headers are included
+from multiple translation units.
+
+.. code-block:: c++
+
+ // Foo.h
+ int a = 1; // Warning: variable definition.
+ extern int d; // OK: extern variable.
+
+ namespace N {
+ int e = 2; // Warning: variable definition.
+ }
+
+ // Warning: variable definition.
+ const char* str = "foo";
+
+ // OK: internal linkage variable definitions are ignored for now.
+ // Although these might also cause ODR violations, we can be less certain and
+ // should try to keep the false-positive rate down.
+ static int b = 1;
+ const int c = 1;
+ const char* const str2 = "foo";
+
+ // Warning: function definition.
+ int g() {
+ return 1;
+ }
+
+ // OK: inline function definition is allowed to be defined multiple times.
+ inline int e() {
+ return 1;
+ }
+
+ class A {
+ public:
+ int f1() { return 1; } // OK: implicitly inline member function definition is allowed.
+ int f2();
+
+ static int d;
+ };
+
+ // Warning: not an inline member function definition.
+ int A::f2() { return 1; }
+
+ // OK: class static data member declaration is allowed.
+ int A::d = 1;
+
+ // OK: function template is allowed.
+ template<typename T>
+ T f3() {
+ T a = 1;
+ return a;
+ }
+
+ // Warning: full specialization of a function template is not allowed.
+ template <>
+ int f3() {
+ int a = 1;
+ return a;
+ }
+
+ template <typename T>
+ struct B {
+ void f1();
+ };
+
+ // OK: member function definition of a class template is allowed.
+ template <typename T>
+ void B<T>::f1() {}
+
+ class CE {
+ constexpr static int i = 5; // OK: inline variable definition.
+ };
+
+ inline int i = 5; // OK: inline variable definition.
+
+ constexpr int k = 1; // OK: constexpr variable has internal linkage.
+
+ constexpr int f10() { return 0; } // OK: constexpr function definition.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+ A comma-separated list of filename extensions of header files (the filename
+ extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+ For header files without an extension, use an empty string (if there are no
+ other desired extensions) or leave an empty element in the list. e.g.,
+ "h,hh,hpp,hxx," (note the trailing comma).
+
+.. option:: UseHeaderFileExtension
+
+ When non-zero, the check will use the file extension to distinguish header
+ files. Default is `1`.
--- /dev/null
+.. title:: clang-tidy - misc-fold-init-type
+
+misc-fold-init-type
+===================
+
+The check flags type mismatches in
+`folds <https://en.wikipedia.org/wiki/Fold_(higher-order_function)>`_
+like ``std::accumulate`` that might result in loss of precision.
+``std::accumulate`` folds an input range into an initial value using the type of
+the latter, with ``operator+`` by default. This can cause loss of precision
+through:
+
+- Truncation: The following code uses a floating point range and an int
+ initial value, so trucation wil happen at every application of ``operator+``
+ and the result will be `0`, which might not be what the user expected.
+
+.. code-block:: c++
+
+ auto a = {0.5f, 0.5f, 0.5f, 0.5f};
+ return std::accumulate(std::begin(a), std::end(a), 0);
+
+- Overflow: The following code also returns `0`.
+
+.. code-block:: c++
+
+ auto a = {65536LL * 65536 * 65536};
+ return std::accumulate(std::begin(a), std::end(a), 0);
--- /dev/null
+.. title:: clang-tidy - misc-forward-declaration-namespace
+
+misc-forward-declaration-namespace
+==================================
+
+Checks if an unused forward declaration is in a wrong namespace.
+
+The check inspects all unused forward declarations and checks if there is any
+declaration/definition with the same name existing, which could indicate that
+the forward declaration is in a potentially wrong namespace.
+
+.. code-block:: c++
+
+ namespace na { struct A; }
+ namespace nb { struct A {}; }
+ nb::A a;
+ // warning : no definition found for 'A', but a definition with the same name
+ // 'A' found in another namespace 'nb::'
+
+This check can only generate warnings, but it can't suggest a fix at this point.
--- /dev/null
+.. title:: clang-tidy - misc-forwarding-reference-overload
+
+misc-forwarding-reference-overload
+==================================
+
+The check looks for perfect forwarding constructors that can hide copy or move
+constructors. If a non const lvalue reference is passed to the constructor, the
+forwarding reference parameter will be a better match than the const reference
+parameter of the copy constructor, so the perfect forwarding constructor will be
+called, which can be confusing.
+For detailed description of this issue see: Scott Meyers, Effective Modern C++,
+Item 26.
+
+Consider the following example:
+
+ .. code-block:: c++
+
+ class Person {
+ public:
+ // C1: perfect forwarding ctor
+ template<typename T>
+ explicit Person(T&& n) {}
+
+ // C2: perfect forwarding ctor with parameter default value
+ template<typename T>
+ explicit Person(T&& n, int x = 1) {}
+
+ // C3: perfect forwarding ctor guarded with enable_if
+ template<typename T, typename X = enable_if_t<is_special<T>,void>>
+ explicit Person(T&& n) {}
+
+ // (possibly compiler generated) copy ctor
+ Person(const Person& rhs);
+ };
+
+The check warns for constructors C1 and C2, because those can hide copy and move
+constructors. We suppress warnings if the copy and the move constructors are both
+disabled (deleted or private), because there is nothing the perfect forwarding
+constructor could hide in this case. We also suppress warnings for constructors
+like C3 that are guarded with an enable_if, assuming the programmer was aware of
+the possible hiding.
+
+Background
+----------
+
+For deciding whether a constructor is guarded with enable_if, we consider the
+default values of the type parameters and the types of the constructor
+parameters. If any part of these types is std::enable_if or std::enable_if_t, we
+assume the constructor is guarded.
--- /dev/null
+.. title:: clang-tidy - misc-inaccurate-erase
+
+misc-inaccurate-erase
+=====================
+
+
+Checks for inaccurate use of the ``erase()`` method.
+
+Algorithms like ``remove()`` do not actually remove any element from the
+container but return an iterator to the first redundant element at the end
+of the container. These redundant elements must be removed using the
+``erase()`` method. This check warns when not all of the elements will be
+removed due to using an inappropriate overload.
--- /dev/null
+.. title:: clang-tidy - misc-incorrect-roundings
+
+misc-incorrect-roundings
+========================
+
+Checks the usage of patterns known to produce incorrect rounding.
+Programmers often use::
+
+ (int)(double_expression + 0.5)
+
+to round the double expression to an integer. The problem with this:
+
+1. It is unnecessarily slow.
+2. It is incorrect. The number 0.499999975 (smallest representable float
+ number below 0.5) rounds to 1.0. Even worse behavior for negative
+ numbers where both -0.5f and -1.4f both round to 0.0.
--- /dev/null
+.. title:: clang-tidy - misc-inefficient-algorithm
+
+misc-inefficient-algorithm
+==========================
+
+
+Warns on inefficient use of STL algorithms on associative containers.
+
+Associative containers implements some of the algorithms as methods which
+should be preferred to the algorithms in the algorithm header. The methods
+can take advanatage of the order of the elements.
+
+.. code-block:: c++
+
+ std::set<int> s;
+ auto it = std::find(s.begin(), s.end(), 43);
+
+ // becomes
+
+ auto it = s.find(43);
+
+.. code-block:: c++
+
+ std::set<int> s;
+ auto c = std::count(s.begin(), s.end(), 43);
+
+ // becomes
+
+ auto c = s.count(43);
--- /dev/null
+.. title:: clang-tidy - misc-lambda-function-name
+
+misc-lambda-function-name
+=========================
+
+Checks for attempts to get the name of a function from within a lambda
+expression. The name of a lambda is always something like ``operator()``, which
+is almost never what was intended.
+
+Example:
+
+.. code-block:: c++
+
+ void FancyFunction() {
+ [] { printf("Called from %s\n", __func__); }();
+ [] { printf("Now called from %s\n", __FUNCTION__); }();
+ }
+
+Output::
+
+ Called from operator()
+ Now called from operator()
+
+Likely intended output::
+
+ Called from FancyFunction
+ Now called from FancyFunction
--- /dev/null
+.. title:: clang-tidy - misc-macro-parentheses
+
+misc-macro-parentheses
+======================
+
+
+Finds macros that can have unexpected behaviour due to missing parentheses.
+
+Macros are expanded by the preprocessor as-is. As a result, there can be
+unexpected behaviour; operators may be evaluated in unexpected order and
+unary operators may become binary operators, etc.
+
+When the replacement list has an expression, it is recommended to surround
+it with parentheses. This ensures that the macro result is evaluated
+completely before it is used.
+
+It is also recommended to surround macro arguments in the replacement list
+with parentheses. This ensures that the argument value is calculated
+properly.
--- /dev/null
+.. title:: clang-tidy - misc-macro-repeated-side-effects
+
+misc-macro-repeated-side-effects
+================================
+
+
+Checks for repeated argument with side effects in macros.
--- /dev/null
+.. title:: clang-tidy - misc-misplaced-const
+
+misc-misplaced-const
+====================
+
+This check diagnoses when a ``const`` qualifier is applied to a ``typedef`` to a
+pointer type rather than to the pointee, because such constructs are often
+misleading to developers because the ``const`` applies to the pointer rather
+than the pointee.
+
+For instance, in the following code, the resulting type is ``int *`` ``const``
+rather than ``const int *``:
+
+.. code-block:: c++
+
+ typedef int *int_ptr;
+ void f(const int_ptr ptr);
+
+The check does not diagnose when the underlying ``typedef`` type is a pointer to
+a ``const`` type or a function pointer type. This is because the ``const``
+qualifier is less likely to be mistaken because it would be redundant (or
+disallowed) on the underlying pointee type.
--- /dev/null
+.. title:: clang-tidy - misc-misplaced-widening-cast
+
+misc-misplaced-widening-cast
+============================
+
+This check will warn when there is a cast of a calculation result to a bigger
+type. If the intention of the cast is to avoid loss of precision then the cast
+is misplaced, and there can be loss of precision. Otherwise the cast is
+ineffective.
+
+Example code:
+
+.. code-block:: c++
+
+ long f(int x) {
+ return (long)(x * 1000);
+ }
+
+The result ``x * 1000`` is first calculated using ``int`` precision. If the
+result exceeds ``int`` precision there is loss of precision. Then the result is
+casted to ``long``.
+
+If there is no loss of precision then the cast can be removed or you can
+explicitly cast to ``int`` instead.
+
+If you want to avoid loss of precision then put the cast in a proper location,
+for instance:
+
+.. code-block:: c++
+
+ long f(int x) {
+ return (long)x * 1000;
+ }
+
+Implicit casts
+--------------
+
+Forgetting to place the cast at all is at least as dangerous and at least as
+common as misplacing it. If :option:`CheckImplicitCasts` is enabled the check
+also detects these cases, for instance:
+
+.. code-block:: c++
+
+ long f(int x) {
+ return x * 1000;
+ }
+
+Floating point
+--------------
+
+Currently warnings are only written for integer conversion. No warning is
+written for this code:
+
+.. code-block:: c++
+
+ double f(float x) {
+ return (double)(x * 10.0f);
+ }
+
+Options
+-------
+
+.. option:: CheckImplicitCasts
+
+ If non-zero, enables detection of implicit casts. Default is non-zero.
--- /dev/null
+.. title:: clang-tidy - misc-move-const-arg
+
+misc-move-const-arg
+===================
+
+The check warns
+
+- if ``std::move()`` is called with a constant argument,
+
+- if ``std::move()`` is called with an argument of a trivially-copyable type,
+
+- if the result of ``std::move()`` is passed as a const reference argument.
+
+In all three cases, the check will suggest a fix that removes the
+``std::move()``.
+
+Here are examples of each of the three cases:
+
+.. code-block:: c++
+
+ const string s;
+ return std::move(s); // Warning: std::move of the const variable has no effect
+
+ int x;
+ return std::move(x); // Warning: std::move of the variable of a trivially-copyable type has no effect
+
+ void f(const string &s);
+ string s;
+ f(std::move(s)); // Warning: passing result of std::move as a const reference argument; no move will actually happen
--- /dev/null
+.. title:: clang-tidy - misc-move-constructor-init
+
+misc-move-constructor-init
+==========================
+
+"cert-oop11-cpp" redirects here as an alias for this check.
+
+The check flags user-defined move constructors that have a ctor-initializer
+initializing a member or base class through a copy constructor instead of a
+move constructor.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - misc-move-forwarding-reference
+
+misc-move-forwarding-reference
+==============================
+
+Warns if ``std::move`` is called on a forwarding reference, for example:
+
+ .. code-block:: c++
+
+ template <typename T>
+ void foo(T&& t) {
+ bar(std::move(t));
+ }
+
+`Forwarding references
+<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf>`_ should
+typically be passed to ``std::forward`` instead of ``std::move``, and this is
+the fix that will be suggested.
+
+(A forwarding reference is an rvalue reference of a type that is a deduced
+function template argument.)
+
+In this example, the suggested fix would be
+
+ .. code-block:: c++
+
+ bar(std::forward<T>(t));
+
+Background
+----------
+
+Code like the example above is sometimes written with the expectation that
+``T&&`` will always end up being an rvalue reference, no matter what type is
+deduced for ``T``, and that it is therefore not possible to pass an lvalue to
+``foo()``. However, this is not true. Consider this example:
+
+ .. code-block:: c++
+
+ std::string s = "Hello, world";
+ foo(s);
+
+This code compiles and, after the call to ``foo()``, ``s`` is left in an
+indeterminate state because it has been moved from. This may be surprising to
+the caller of ``foo()`` because no ``std::move`` was used when calling
+``foo()``.
+
+The reason for this behavior lies in the special rule for template argument
+deduction on function templates like ``foo()`` -- i.e. on function templates
+that take an rvalue reference argument of a type that is a deduced function
+template argument. (See section [temp.deduct.call]/3 in the C++11 standard.)
+
+If ``foo()`` is called on an lvalue (as in the example above), then ``T`` is
+deduced to be an lvalue reference. In the example, ``T`` is deduced to be
+``std::string &``. The type of the argument ``t`` therefore becomes
+``std::string& &&``; by the reference collapsing rules, this collapses to
+``std::string&``.
+
+This means that the ``foo(s)`` call passes ``s`` as an lvalue reference, and
+``foo()`` ends up moving ``s`` and thereby placing it into an indeterminate
+state.
--- /dev/null
+.. title:: clang-tidy - misc-multiple-statement-macro
+
+misc-multiple-statement-macro
+=============================
+
+Detect multiple statement macros that are used in unbraced conditionals. Only
+the first statement of the macro will be inside the conditional and the other
+ones will be executed unconditionally.
+
+Example:
+
+.. code-block:: c++
+
+ #define INCREMENT_TWO(x, y) (x)++; (y)++
+ if (do_increment)
+ INCREMENT_TWO(a, b); // (b)++ will be executed unconditionally.
--- /dev/null
+.. title:: clang-tidy - misc-new-delete-overloads
+
+misc-new-delete-overloads
+=========================
+
+`cert-dcl54-cpp` redirects here as an alias for this check.
+
+The check flags overloaded operator ``new()`` and operator ``delete()``
+functions that do not have a corresponding free store function defined within
+the same scope.
+For instance, the check will flag a class implementation of a non-placement
+operator ``new()`` when the class does not also define a non-placement operator
+``delete()`` function as well.
+
+The check does not flag implicitly-defined operators, deleted or private
+operators, or placement operators.
+
+This check corresponds to CERT C++ Coding Standard rule `DCL54-CPP. Overload allocation and deallocation functions as a pair in the same scope
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL54-CPP.+Overload+allocation+and+deallocation+functions+as+a+pair+in+the+same+scope>`_.
--- /dev/null
+.. title:: clang-tidy - misc-noexcept-move-constructor
+
+misc-noexcept-move-constructor
+==============================
+
+
+The check flags user-defined move constructors and assignment operators not
+marked with ``noexcept`` or marked with ``noexcept(expr)`` where ``expr``
+evaluates to ``false`` (but is not a ``false`` literal itself).
+
+Move constructors of all the types used with STL containers, for example,
+need to be declared ``noexcept``. Otherwise STL will choose copy constructors
+instead. The same is valid for move assignment operations.
--- /dev/null
+.. title:: clang-tidy - misc-non-copyable-objects
+
+misc-non-copyable-objects
+=========================
+
+`cert-fio38-c` redirects here as an alias for this check.
+
+The check flags dereferences and non-pointer declarations of objects that are
+not meant to be passed by value, such as C FILE objects or POSIX
+``pthread_mutex_t`` objects.
+
+This check corresponds to CERT C++ Coding Standard rule `FIO38-C. Do not copy a FILE object
+<https://www.securecoding.cert.org/confluence/display/c/FIO38-C.+Do+not+copy+a+FILE+object>`_.
--- /dev/null
+.. title:: clang-tidy - misc-redundant-expression
+
+misc-redundant-expression
+=========================
+
+Detect redundant expressions which are typically errors due to copy-paste.
+
+Depending on the operator expressions may be
+
+- redundant,
+
+- always be ``true``,
+
+- always be ``false``,
+
+- always be a constant (zero or one).
+
+Example:
+
+.. code-block:: c++
+
+ ((x+1) | (x+1)) // (x+1) is redundant
+ (p->x == p->x) // always true
+ (p->x < p->x) // always false
+ (speed - speed + 1 == 12) // speed - speed is always zero
--- /dev/null
+.. title:: clang-tidy - misc-sizeof-container
+
+misc-sizeof-container
+=====================
+
+The check finds usages of ``sizeof`` on expressions of STL container types. Most
+likely the user wanted to use ``.size()`` instead.
+
+All class/struct types declared in namespace ``std::`` having a const ``size()``
+method are considered containers, with the exception of ``std::bitset`` and
+``std::array``.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string s;
+ int a = 47 + sizeof(s); // warning: sizeof() doesn't return the size of the container. Did you mean .size()?
+
+ int b = sizeof(std::string); // no warning, probably intended.
+
+ std::string array_of_strings[10];
+ int c = sizeof(array_of_strings) / sizeof(array_of_strings[0]); // no warning, definitely intended.
+
+ std::array<int, 3> std_array;
+ int d = sizeof(std_array); // no warning, probably intended.
--- /dev/null
+.. title:: clang-tidy - misc-sizeof-expression
+
+misc-sizeof-expression
+======================
+
+The check finds usages of ``sizeof`` expressions which are most likely errors.
+
+The ``sizeof`` operator yields the size (in bytes) of its operand, which may be
+an expression or the parenthesized name of a type. Misuse of this operator may
+be leading to errors and possible software vulnerabilities.
+
+Suspicious usage of 'sizeof(K)'
+-------------------------------
+
+A common mistake is to query the ``sizeof`` of an integer literal. This is
+equivalent to query the size of its type (probably ``int``). The intent of the
+programmer was probably to simply get the integer and not its size.
+
+.. code-block:: c++
+
+ #define BUFLEN 42
+ char buf[BUFLEN];
+ memset(buf, 0, sizeof(BUFLEN)); // sizeof(42) ==> sizeof(int)
+
+Suspicious usage of 'sizeof(this)'
+----------------------------------
+
+The ``this`` keyword is evaluated to a pointer to an object of a given type.
+The expression ``sizeof(this)`` is returning the size of a pointer. The
+programmer most likely wanted the size of the object and not the size of the
+pointer.
+
+.. code-block:: c++
+
+ class Point {
+ [...]
+ size_t size() { return sizeof(this); } // should probably be sizeof(*this)
+ [...]
+ };
+
+Suspicious usage of 'sizeof(char*)'
+-----------------------------------
+
+There is a subtle difference between declaring a string literal with
+``char* A = ""`` and ``char A[] = ""``. The first case has the type ``char*``
+instead of the aggregate type ``char[]``. Using ``sizeof`` on an object declared
+with ``char*`` type is returning the size of a pointer instead of the number of
+characters (bytes) in the string literal.
+
+.. code-block:: c++
+
+ const char* kMessage = "Hello World!"; // const char kMessage[] = "...";
+ void getMessage(char* buf) {
+ memcpy(buf, kMessage, sizeof(kMessage)); // sizeof(char*)
+ }
+
+Suspicious usage of 'sizeof(A*)'
+--------------------------------
+
+A common mistake is to compute the size of a pointer instead of its pointee.
+These cases may occur because of explicit cast or implicit conversion.
+
+.. code-block:: c++
+
+ int A[10];
+ memset(A, 0, sizeof(A + 0));
+
+ struct Point point;
+ memset(point, 0, sizeof(&point));
+
+Suspicious usage of 'sizeof(...)/sizeof(...)'
+---------------------------------------------
+
+Dividing ``sizeof`` expressions is typically used to retrieve the number of
+elements of an aggregate. This check warns on incompatible or suspicious cases.
+
+In the following example, the entity has 10-bytes and is incompatible with the
+type ``int`` which has 4 bytes.
+
+.. code-block:: c++
+
+ char buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // sizeof(buf) => 10
+ void getMessage(char* dst) {
+ memcpy(dst, buf, sizeof(buf) / sizeof(int)); // sizeof(int) => 4 [incompatible sizes]
+ }
+
+In the following example, the expression ``sizeof(Values)`` is returning the
+size of ``char*``. One can easily be fooled by its declaration, but in parameter
+declaration the size '10' is ignored and the function is receiving a ``char*``.
+
+.. code-block:: c++
+
+ char OrderedValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ return CompareArray(char Values[10]) {
+ return memcmp(OrderedValues, Values, sizeof(Values)) == 0; // sizeof(Values) ==> sizeof(char*) [implicit cast to char*]
+ }
+
+Suspicious 'sizeof' by 'sizeof' expression
+------------------------------------------
+
+Multiplying ``sizeof`` expressions typically makes no sense and is probably a
+logic error. In the following example, the programmer used ``*`` instead of
+``/``.
+
+.. code-block:: c++
+
+ const char kMessage[] = "Hello World!";
+ void getMessage(char* buf) {
+ memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char)); // sizeof(kMessage) / sizeof(char)
+ }
+
+This check may trigger on code using the arraysize macro. The following code is
+working correctly but should be simplified by using only the ``sizeof``
+operator.
+
+.. code-block:: c++
+
+ extern Object objects[100];
+ void InitializeObjects() {
+ memset(objects, 0, arraysize(objects) * sizeof(Object)); // sizeof(objects)
+ }
+
+Suspicious usage of 'sizeof(sizeof(...))'
+-----------------------------------------
+
+Getting the ``sizeof`` of a ``sizeof`` makes no sense and is typically an error
+hidden through macros.
+
+.. code-block:: c++
+
+ #define INT_SZ sizeof(int)
+ int buf[] = { 42 };
+ void getInt(int* dst) {
+ memcpy(dst, buf, sizeof(INT_SZ)); // sizeof(sizeof(int)) is suspicious.
+ }
+
+Options
+-------
+
+.. option:: WarnOnSizeOfConstant
+
+ When non-zero, the check will warn on an expression like
+ ``sizeof(CONSTANT)``. Default is `1`.
+
+.. option:: WarnOnSizeOfThis
+
+ When non-zero, the check will warn on an expression like ``sizeof(this)``.
+ Default is `1`.
+
+.. option:: WarnOnSizeOfCompareToConstant
+
+ When non-zero, the check will warn on an expression like
+ ``sizeof(epxr) <= k`` for a suspicious constant `k` while `k` is `0` or
+ greater than `0x8000`. Default is `1`.
--- /dev/null
+.. title:: clang-tidy - misc-static-assert
+
+misc-static-assert
+==================
+
+`cert-dcl03-c` redirects here as an alias for this check.
+
+Replaces ``assert()`` with ``static_assert()`` if the condition is evaluatable
+at compile time.
+
+The condition of ``static_assert()`` is evaluated at compile time which is
+safer and more efficient.
--- /dev/null
+.. title:: clang-tidy - misc-string-compare
+
+misc-string-compare
+===================
+
+Finds string comparisons using the compare method.
+
+A common mistake is to use the string's ``compare`` method instead of using the
+equality or inequality operators. The compare method is intended for sorting
+functions and thus returns a negative number, a positive number or
+zero depending on the lexicographical relationship between the strings compared.
+If an equality or inequality check can suffice, that is recommended. This is
+recommended to avoid the risk of incorrect interpretation of the return value
+and to simplify the code. The string equality and inequality operators can
+also be faster than the ``compare`` method due to early termination.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string str1{"a"};
+ std::string str2{"b"};
+
+ // use str1 != str2 instead.
+ if (str1.compare(str2)) {
+ }
+
+ // use str1 == str2 instead.
+ if (!str1.compare(str2)) {
+ }
+
+ // use str1 == str2 instead.
+ if (str1.compare(str2) == 0) {
+ }
+
+ // use str1 != str2 instead.
+ if (str1.compare(str2) != 0) {
+ }
+
+ // use str1 == str2 instead.
+ if (0 == str1.compare(str2)) {
+ }
+
+ // use str1 != str2 instead.
+ if (0 != str1.compare(str2)) {
+ }
+
+ // Use str1 == "foo" instead.
+ if (str1.compare("foo") == 0) {
+ }
+
+The above code examples shows the list of if-statements that this check will
+give a warning for. All of them uses ``compare`` to check if equality or
+inequality of two strings instead of using the correct operators.
--- /dev/null
+.. title:: clang-tidy - misc-string-constructor
+
+misc-string-constructor
+=======================
+
+Finds string constructors that are suspicious and probably errors.
+
+A common mistake is to swap parameters to the 'fill' string-constructor.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string('x', 50) str; // should be std::string(50, 'x')
+
+Calling the string-literal constructor with a length bigger than the literal is
+suspicious and adds extra random characters to the string.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string("test", 200); // Will include random characters after "test".
+
+Creating an empty string from constructors with parameters is considered
+suspicious. The programmer should use the empty constructor instead.
+
+Examples:
+
+.. code-block:: c++
+
+ std::string("test", 0); // Creation of an empty string.
+
+Options
+-------
+
+.. option:: WarnOnLargeLength
+
+ When non-zero, the check will warn on a string with a length greater than
+ `LargeLengthThreshold`. Default is `1`.
+
+.. option:: LargeLengthThreshold
+
+ An integer specifying the large length threshold. Default is `0x800000`.
--- /dev/null
+.. title:: clang-tidy - misc-string-integer-assignment
+
+misc-string-integer-assignment
+==============================
+
+The check finds assignments of an integer to ``std::basic_string<CharT>``
+(``std::string``, ``std::wstring``, etc.). The source of the problem is the
+following assignment operator of ``std::basic_string<CharT>``:
+
+.. code-block:: c++
+
+ basic_string& operator=( CharT ch );
+
+Numeric types can be implicitly casted to character types.
+
+.. code-block:: c++
+
+ std::string s;
+ int x = 5965;
+ s = 6;
+ s = x;
+
+Use the appropriate conversion functions or character literals.
+
+.. code-block:: c++
+
+ std::string s;
+ int x = 5965;
+ s = '6';
+ s = std::to_string(x);
+
+In order to suppress false positives, use an explicit cast.
+
+.. code-block:: c++
+
+ std::string s;
+ s = static_cast<char>(6);
--- /dev/null
+.. title:: clang-tidy - misc-string-literal-with-embedded-nul
+
+misc-string-literal-with-embedded-nul
+=====================================
+
+Finds occurrences of string literal with embedded NUL character and validates
+their usage.
+
+Invalid escaping
+----------------
+
+Special characters can be escaped within a string literal by using their
+hexadecimal encoding like ``\x42``. A common mistake is to escape them
+like this ``\0x42`` where the ``\0`` stands for the NUL character.
+
+.. code-block:: c++
+
+ const char* Example[] = "Invalid character: \0x12 should be \x12";
+ const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF";
+
+Truncated literal
+-----------------
+
+String-like classes can manipulate strings with embedded NUL as they are keeping
+track of the bytes and the length. This is not the case for a ``char*``
+(NUL-terminated) string.
+
+A common mistake is to pass a string-literal with embedded NUL to a string
+constructor expecting a NUL-terminated string. The bytes after the first NUL
+character are truncated.
+
+.. code-block:: c++
+
+ std::string str("abc\0def"); // "def" is truncated
+ str += "\0"; // This statement is doing nothing
+ if (str == "\0abc") return; // This expression is always true
--- /dev/null
+.. title:: clang-tidy - misc-suspicious-enum-usage
+
+misc-suspicious-enum-usage
+==========================
+
+The checker detects various cases when an enum is probably misused (as a bitmask
+).
+
+1. When "ADD" or "bitwise OR" is used between two enum which come from different
+ types and these types value ranges are not disjoint.
+
+The following cases will be investigated only using :option:`StrictMode`. We
+regard the enum as a (suspicious)
+bitmask if the three conditions below are true at the same time:
+
+* at most half of the elements of the enum are non pow-of-2 numbers (because of
+ short enumerations)
+* there is another non pow-of-2 number than the enum constant representing all
+ choices (the result "bitwise OR" operation of all enum elements)
+* enum type variable/enumconstant is used as an argument of a `+` or "bitwise OR
+ " operator
+
+So whenever the non pow-of-2 element is used as a bitmask element we diagnose a
+misuse and give a warning.
+
+2. Investigating the right hand side of `+=` and `|=` operator.
+3. Check only the enum value side of a `|` and `+` operator if one of them is not
+ enum val.
+4. Check both side of `|` or `+` operator where the enum values are from the
+ same enum type.
+
+Examples:
+
+.. code-block:: c++
+
+ enum { A, B, C };
+ enum { D, E, F = 5 };
+ enum { G = 10, H = 11, I = 12 };
+
+ unsigned flag;
+ flag =
+ A |
+ H; // OK, disjoint value intervalls in the enum types ->probably good use.
+ flag = B | F; // Warning, have common values so they are probably misused.
+
+ // Case 2:
+ enum Bitmask {
+ A = 0,
+ B = 1,
+ C = 2,
+ D = 4,
+ E = 8,
+ F = 16,
+ G = 31 // OK, real bitmask.
+ };
+
+ enum Almostbitmask {
+ AA = 0,
+ BB = 1,
+ CC = 2,
+ DD = 4,
+ EE = 8,
+ FF = 16,
+ GG // Problem, forgot to initialize.
+ };
+
+ unsigned flag = 0;
+ flag |= E; // OK.
+ flag |=
+ EE; // Warning at the decl, and note that it was used here as a bitmask.
+
+Options
+-------
+.. option:: StrictMode
+
+ Default value: 0.
+ When non-null the suspicious bitmask usage will be investigated additionally
+ to the different enum usage check.
--- /dev/null
+.. title:: clang-tidy - misc-suspicious-missing-comma
+
+misc-suspicious-missing-comma
+=============================
+
+String literals placed side-by-side are concatenated at translation phase 6
+(after the preprocessor). This feature is used to represent long string
+literal on multiple lines.
+
+For instance, the following declarations are equivalent:
+
+.. code-block:: c++
+
+ const char* A[] = "This is a test";
+ const char* B[] = "This" " is a " "test";
+
+A common mistake done by programmers is to forget a comma between two string
+literals in an array initializer list.
+
+.. code-block:: c++
+
+ const char* Test[] = {
+ "line 1",
+ "line 2" // Missing comma!
+ "line 3",
+ "line 4",
+ "line 5"
+ };
+
+The array contains the string "line 2line3" at offset 1 (i.e. Test[1]). Clang
+won't generate warnings at compile time.
+
+This check may warn incorrectly on cases like:
+
+.. code-block:: c++
+
+ const char* SupportedFormat[] = {
+ "Error %s",
+ "Code " PRIu64, // May warn here.
+ "Warning %s",
+ };
+
+Options
+-------
+
+.. option:: SizeThreshold
+
+ An unsigned integer specifying the minimum size of a string literal to be
+ considered by the check. Default is `5U`.
+
+.. option:: RatioThreshold
+
+ A string specifying the maximum threshold ratio [0, 1.0] of suspicious string
+ literals to be considered. Default is `".2"`.
+
+.. option:: MaxConcatenatedTokens
+
+ An unsigned integer specifying the maximum number of concatenated tokens.
+ Default is `5U`.
--- /dev/null
+.. title:: clang-tidy - misc-suspicious-semicolon
+
+misc-suspicious-semicolon
+=========================
+
+Finds most instances of stray semicolons that unexpectedly alter the meaning of
+the code. More specifically, it looks for ``if``, ``while``, ``for`` and
+``for-range`` statements whose body is a single semicolon, and then analyzes the
+context of the code (e.g. indentation) in an attempt to determine whether that
+is intentional.
+
+ .. code-block:: c++
+
+ if (x < y);
+ {
+ x++;
+ }
+
+Here the body of the ``if`` statement consists of only the semicolon at the end
+of the first line, and `x` will be incremented regardless of the condition.
+
+
+ .. code-block:: c++
+
+ while ((line = readLine(file)) != NULL);
+ processLine(line);
+
+As a result of this code, `processLine()` will only be called once, when the
+``while`` loop with the empty body exits with `line == NULL`. The indentation of
+the code indicates the intention of the programmer.
+
+
+ .. code-block:: c++
+
+ if (x >= y);
+ x -= y;
+
+While the indentation does not imply any nesting, there is simply no valid
+reason to have an `if` statement with an empty body (but it can make sense for
+a loop). So this check issues a warning for the code above.
+
+To solve the issue remove the stray semicolon or in case the empty body is
+intentional, reflect this using code indentation or put the semicolon in a new
+line. For example:
+
+ .. code-block:: c++
+
+ while (readWhitespace());
+ Token t = readNextToken();
+
+Here the second line is indented in a way that suggests that it is meant to be
+the body of the `while` loop - whose body is in fact empty, because of the
+semicolon at the end of the first line.
+
+Either remove the indentation from the second line:
+
+ .. code-block:: c++
+
+ while (readWhitespace());
+ Token t = readNextToken();
+
+... or move the semicolon from the end of the first line to a new line:
+
+ .. code-block:: c++
+
+ while (readWhitespace())
+ ;
+
+ Token t = readNextToken();
+
+In this case the check will assume that you know what you are doing, and will
+not raise a warning.
--- /dev/null
+.. title:: clang-tidy - misc-suspicious-string-compare
+
+misc-suspicious-string-compare
+==============================
+
+Find suspicious usage of runtime string comparison functions.
+This check is valid in C and C++.
+
+Checks for calls with implicit comparator and proposed to explicitly add it.
+
+.. code-block:: c++
+
+ if (strcmp(...)) // Implicitly compare to zero
+ if (!strcmp(...)) // Won't warn
+ if (strcmp(...) != 0) // Won't warn
+
+Checks that compare function results (i,e, ``strcmp``) are compared to valid
+constant. The resulting value is
+
+.. code::
+
+ < 0 when lower than,
+ > 0 when greater than,
+ == 0 when equals.
+
+A common mistake is to compare the result to `1` or `-1`.
+
+.. code-block:: c++
+
+ if (strcmp(...) == -1) // Incorrect usage of the returned value.
+
+Additionally, the check warns if the results value is implicitly cast to a
+*suspicious* non-integer type. It's happening when the returned value is used in
+a wrong context.
+
+.. code-block:: c++
+
+ if (strcmp(...) < 0.) // Incorrect usage of the returned value.
+
+Options
+-------
+
+.. option:: WarnOnImplicitComparison
+
+ When non-zero, the check will warn on implicit comparison. `1` by default.
+
+.. option:: WarnOnLogicalNotComparison
+
+ When non-zero, the check will warn on logical not comparison. `0` by default.
+
+.. option:: StringCompareLikeFunctions
+
+ A string specifying the comma-separated names of the extra string comparison
+ functions. Default is an empty string.
+ The check will detect the following string comparison functions:
+ `__builtin_memcmp`, `__builtin_strcasecmp`, `__builtin_strcmp`,
+ `__builtin_strncasecmp`, `__builtin_strncmp`, `_mbscmp`, `_mbscmp_l`,
+ `_mbsicmp`, `_mbsicmp_l`, `_mbsnbcmp`, `_mbsnbcmp_l`, `_mbsnbicmp`,
+ `_mbsnbicmp_l`, `_mbsncmp`, `_mbsncmp_l`, `_mbsnicmp`, `_mbsnicmp_l`,
+ `_memicmp`, `_memicmp_l`, `_stricmp`, `_stricmp_l`, `_strnicmp`,
+ `_strnicmp_l`, `_wcsicmp`, `_wcsicmp_l`, `_wcsnicmp`, `_wcsnicmp_l`,
+ `lstrcmp`, `lstrcmpi`, `memcmp`, `memicmp`, `strcasecmp`, `strcmp`,
+ `strcmpi`, `stricmp`, `strncasecmp`, `strncmp`, `strnicmp`, `wcscasecmp`,
+ `wcscmp`, `wcsicmp`, `wcsncmp`, `wcsnicmp`, `wmemcmp`.
--- /dev/null
+.. title:: clang-tidy - misc-swapped-arguments
+
+misc-swapped-arguments
+======================
+
+
+Finds potentially swapped arguments by looking at implicit conversions.
--- /dev/null
+.. title:: clang-tidy - misc-throw-by-value-catch-by-reference
+
+misc-throw-by-value-catch-by-reference
+======================================
+
+"cert-err09-cpp" redirects here as an alias for this check.
+"cert-err61-cpp" redirects here as an alias for this check.
+
+Finds violations of the rule "Throw by value, catch by reference" presented for
+example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu.
+
+Exceptions:
+ * Throwing string literals will not be flagged despite being a pointer. They
+ are not susceptible to slicing and the usage of string literals is idomatic.
+ * Catching character pointers (``char``, ``wchar_t``, unicode character types)
+ will not be flagged to allow catching sting literals.
+ * Moved named values will not be flagged as not throwing an anonymous
+ temporary. In this case we can be sure that the user knows that the object
+ can't be accessed outside catch blocks handling the error.
+ * Throwing function parameters will not be flagged as not throwing an
+ anonymous temporary. This allows helper functions for throwing.
+ * Re-throwing caught exception variables will not be flragged as not throwing
+ an anonymous temporary. Although this can usually be done by just writing
+ ``throw;`` it happens often enough in real code.
+
+Options
+-------
+
+.. option:: CheckThrowTemporaries
+
+ Triggers detection of violations of the rule `Throw anonymous temporaries
+ <https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries>`_.
+ Default is `1`.
+
--- /dev/null
+.. title:: clang-tidy - misc-unconventional-assign-operator
+
+misc-unconventional-assign-operator
+===================================
+
+
+Finds declarations of assign operators with the wrong return and/or argument
+types and definitions with good return type but wrong ``return`` statements.
+
+ * The return type must be ``Class&``.
+ * Works with move-assign and assign by value.
+ * Private and deleted operators are ignored.
+ * The operator must always return ``*this``.
--- /dev/null
+.. title:: clang-tidy - misc-undelegated-constructor
+
+misc-undelegated-constructor
+============================
+
+
+Finds creation of temporary objects in constructors that look like a
+function call to another constructor of the same class.
+
+The user most likely meant to use a delegating constructor or base class
+initializer.
--- /dev/null
+.. title:: clang-tidy - misc-uniqueptr-reset-release
+
+misc-uniqueptr-reset-release
+============================
+
+Find and replace ``unique_ptr::reset(release())`` with ``std::move()``.
+
+Example:
+
+.. code-block:: c++
+
+ std::unique_ptr<Foo> x, y;
+ x.reset(y.release()); -> x = std::move(y);
+
+If ``y`` is already rvalue, ``std::move()`` is not added. ``x`` and ``y`` can
+also be ``std::unique_ptr<Foo>*``.
--- /dev/null
+.. title:: clang-tidy - misc-unused-alias-decls
+
+misc-unused-alias-decls
+=======================
+
+
+Finds unused namespace alias declarations.
--- /dev/null
+.. title:: clang-tidy - misc-unused-parameters
+
+misc-unused-parameters
+======================
+
+Finds unused parameters and fixes them, so that `-Wunused-parameter` can be
+turned on.
+
+.. code-block:: c++
+
+ void a(int i) {}
+
+ // becomes
+
+ void a(int /*i*/) {}
+
+
+.. code-block:: c++
+
+ static void staticFunctionA(int i);
+ static void staticFunctionA(int i) {}
+
+ // becomes
+
+ static void staticFunctionA()
+ static void staticFunctionA() {}
--- /dev/null
+.. title:: clang-tidy - misc-unused-raii
+
+misc-unused-raii
+================
+
+Finds temporaries that look like RAII objects.
+
+The canonical example for this is a scoped lock.
+
+.. code-block:: c++
+
+ {
+ scoped_lock(&global_mutex);
+ critical_section();
+ }
+
+The destructor of the scoped_lock is called before the ``critical_section`` is
+entered, leaving it unprotected.
+
+We apply a number of heuristics to reduce the false positive count of this
+check:
+
+- Ignore code expanded from macros. Testing frameworks make heavy use of this.
+
+- Ignore types with trivial destructors. They are very unlikely to be RAII
+ objects and there's no difference when they are deleted.
+
+- Ignore objects at the end of a compound statement (doesn't change behavior).
+
+- Ignore objects returned from a call.
--- /dev/null
+.. title:: clang-tidy - misc-unused-using-decls
+
+misc-unused-using-decls
+=======================
+
+Finds unused ``using`` declarations.
+
+Example:
+
+.. code-block:: c++
+
+ namespace n { class C; }
+ using n::C; // Never actually used.
--- /dev/null
+.. title:: clang-tidy - misc-use-after-move
+
+misc-use-after-move
+===================
+
+Warns if an object is used after it has been moved, for example:
+
+ .. code-block:: c++
+
+ std::string str = "Hello, world!\n";
+ std::vector<std::string> messages;
+ messages.emplace_back(std::move(str));
+ std::cout << str;
+
+The last line will trigger a warning that ``str`` is used after it has been
+moved.
+
+The check does not trigger a warning if the object is reinitialized after the
+move and before the use. For example, no warning will be output for this code:
+
+ .. code-block:: c++
+
+ messages.emplace_back(std::move(str));
+ str = "Greetings, stranger!\n";
+ std::cout << str;
+
+The check takes control flow into account. A warning is only emitted if the use
+can be reached from the move. This means that the following code does not
+produce a warning:
+
+ .. code-block:: c++
+
+ if (condition) {
+ messages.emplace_back(std::move(str));
+ } else {
+ std::cout << str;
+ }
+
+On the other hand, the following code does produce a warning:
+
+ .. code-block:: c++
+
+ for (int i = 0; i < 10; ++i) {
+ std::cout << str;
+ messages.emplace_back(std::move(str));
+ }
+
+(The use-after-move happens on the second iteration of the loop.)
+
+In some cases, the check may not be able to detect that two branches are
+mutually exclusive. For example (assuming that ``i`` is an int):
+
+ .. code-block:: c++
+
+ if (i == 1) {
+ messages.emplace_back(std::move(str));
+ }
+ if (i == 2) {
+ std::cout << str;
+ }
+
+In this case, the check will erroneously produce a warning, even though it is
+not possible for both the move and the use to be executed.
+
+An erroneous warning can be silenced by reinitializing the object after the
+move:
+
+ .. code-block:: c++
+
+ if (i == 1) {
+ messages.emplace_back(std::move(str));
+ str = "";
+ }
+ if (i == 2) {
+ std::cout << str;
+ }
+
+Subsections below explain more precisely what exactly the check considers to be
+a move, use, and reinitialization.
+
+Unsequenced moves, uses, and reinitializations
+----------------------------------------------
+
+In many cases, C++ does not make any guarantees about the order in which
+sub-expressions of a statement are evaluated. This means that in code like the
+following, it is not guaranteed whether the use will happen before or after the
+move:
+
+ .. code-block:: c++
+
+ void f(int i, std::vector<int> v);
+ std::vector<int> v = { 1, 2, 3 };
+ f(v[1], std::move(v));
+
+In this kind of situation, the check will note that the use and move are
+unsequenced.
+
+The check will also take sequencing rules into account when reinitializations
+occur in the same statement as moves or uses. A reinitialization is only
+considered to reinitialize a variable if it is guaranteed to be evaluated after
+the move and before the use.
+
+Move
+----
+
+The check currently only considers calls of ``std::move`` on local variables or
+function parameters. It does not check moves of member variables or global
+variables.
+
+Any call of ``std::move`` on a variable is considered to cause a move of that
+variable, even if the result of ``std::move`` is not passed to an rvalue
+reference parameter.
+
+This means that the check will flag a use-after-move even on a type that does
+not define a move constructor or move assignment operator. This is intentional.
+Developers may use ``std::move`` on such a type in the expectation that the type
+will add move semantics in the future. If such a ``std::move`` has the potential
+to cause a use-after-move, we want to warn about it even if the type does not
+implement move semantics yet.
+
+Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference
+parameter, this will always be considered to cause a move, even if the function
+that consumes this parameter does not move from it, or if it does so only
+conditionally. For example, in the following situation, the check will assume
+that a move always takes place:
+
+ .. code-block:: c++
+
+ std::vector<std::string> messages;
+ void f(std::string &&str) {
+ // Only remember the message if it isn't empty.
+ if (!str.empty()) {
+ messages.emplace_back(std::move(str));
+ }
+ }
+ std::string str = "";
+ f(std::move(str));
+
+The check will assume that the last line causes a move, even though, in this
+particular case, it does not. Again, this is intentional.
+
+When analyzing the order in which moves, uses and reinitializations happen (see
+section `Unsequenced moves, uses, and reinitializations`_), the move is assumed
+to occur in whichever function the result of the ``std::move`` is passed to.
+
+Use
+---
+
+Any occurrence of the moved variable that is not a reinitialization (see below)
+is considered to be a use.
+
+An exception to this are objects of type ``std::unique_ptr``,
+``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior
+(objects of these classes are guaranteed to be empty after they have been moved
+from). Therefore, an object of these classes will only be considered to be used
+if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
+(in the case of ``std::unique_ptr<T []>``) is called on it.
+
+If multiple uses occur after a move, only the first of these is flagged.
+
+Reinitialization
+----------------
+
+The check considers a variable to be reinitialized in the following cases:
+
+ - The variable occurs on the left-hand side of an assignment.
+
+ - The variable is passed to a function as a non-const pointer or non-const
+ lvalue reference. (It is assumed that the variable may be an out-parameter
+ for the function.)
+
+ - ``clear()`` or ``assign()`` is called on the variable and the variable is of
+ one of the standard container types ``basic_string``, ``vector``, ``deque``,
+ ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``,
+ ``unordered_set``, ``unordered_map``, ``unordered_multiset``,
+ ``unordered_multimap``.
+
+ - ``reset()`` is called on the variable and the variable is of type
+ ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``.
+
+If the variable in question is a struct and an individual member variable of
+that struct is written to, the check does not consider this to be a
+reinitialization -- even if, eventually, all member variables of the struct are
+written to. For example:
+
+ .. code-block:: c++
+
+ struct S {
+ std::string str;
+ int i;
+ };
+ S s = { "Hello, world!\n", 42 };
+ S s_other = std::move(s);
+ s.str = "Lorem ipsum";
+ s.i = 99;
+
+The check will not consider ``s`` to be reinitialized after the last line;
+instead, the line that assigns to ``s.str`` will be flagged as a use-after-move.
+This is intentional as this pattern of reinitializing a struct is error-prone.
+For example, if an additional member variable is added to ``S``, it is easy to
+forget to add the reinitialization for this additional member. Instead, it is
+safer to assign to the entire struct in one go, and this will also avoid the
+use-after-move warning.
--- /dev/null
+.. title:: clang-tidy - misc-virtual-near-miss
+
+misc-virtual-near-miss
+======================
+
+Warn if a function is a near miss (ie. the name is very similar and the function
+signiture is the same) to a virtual function from a base class.
+
+Example:
+
+.. code-block:: c++
+
+ struct Base {
+ virtual void func();
+ };
+
+ struct Derived : Base {
+ virtual funk();
+ // warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it?
+ };
--- /dev/null
+.. title:: clang-tidy - modernize-avoid-bind
+
+modernize-avoid-bind
+====================
+
+The check finds uses of ``std::bind`` and replaces simple uses with lambdas.
+Lambdas will use value-capture where required.
+
+Right now it only handles free functions, not member functions.
+
+Given:
+
+.. code-block:: c++
+
+ int add(int x, int y) { return x + y; }
+
+Then:
+
+.. code-block:: c++
+
+ void f() {
+ int x = 2;
+ auto clj = std::bind(add, x, _1);
+ }
+
+is replaced by:
+
+.. code-block:: c++
+
+ void f() {
+ int x = 2;
+ auto clj = [=](auto && arg1) { return add(x, arg1); };
+ }
+
+``std::bind`` can be hard to read and can result in larger object files and
+binaries due to type information that will not be produced by equivalent
+lambdas.
--- /dev/null
+.. title:: clang-tidy - modernize-deprecated-headers
+
+modernize-deprecated-headers
+============================
+
+Some headers from C library were deprecated in C++ and are no longer welcome in
+C++ codebases. Some have no effect in C++. For more details refer to the C++ 14
+Standard [depr.c.headers] section.
+
+This check replaces C standard library headers with their C++ alternatives and
+removes redundant ones.
+
+Improtant note: the Standard doesn't guarantee that the C++ headers declare all
+the same functions in the global namespace. The check in its current form can
+break the code that uses library symbols from the global namespace.
+
+* `<assert.h>`
+* `<complex.h>`
+* `<ctype.h>`
+* `<errno.h>`
+* `<fenv.h>` // deprecated since C++11
+* `<float.h>`
+* `<inttypes.h>`
+* `<limits.h>`
+* `<locale.h>`
+* `<math.h>`
+* `<setjmp.h>`
+* `<signal.h>`
+* `<stdarg.h>`
+* `<stddef.h>`
+* `<stdint.h>`
+* `<stdio.h>`
+* `<stdlib.h>`
+* `<string.h>`
+* `<tgmath.h>` // deprecated since C++11
+* `<time.h>`
+* `<uchar.h>` // deprecated since C++11
+* `<wchar.h>`
+* `<wctype.h>`
+
+If the specified standard is older than C++11 the check will only replace
+headers deprecated before C++11, otherwise -- every header that appeared in
+the previous list.
+
+These headers don't have effect in C++:
+
+* `<iso646.h>`
+* `<stdalign.h>`
+* `<stdbool.h>`
--- /dev/null
+.. title:: clang-tidy - modernize-loop-convert
+
+modernize-loop-convert
+======================
+
+This check converts ``for(...; ...; ...)`` loops to use the new range-based
+loops in C++11.
+
+Three kinds of loops can be converted:
+
+- Loops over statically allocated arrays.
+- Loops over containers, using iterators.
+- Loops over array-like containers, using ``operator[]`` and ``at()``.
+
+MinConfidence option
+--------------------
+
+risky
+^^^^^
+
+In loops where the container expression is more complex than just a
+reference to a declared expression (a variable, function, enum, etc.),
+and some part of it appears elsewhere in the loop, we lower our confidence
+in the transformation due to the increased risk of changing semantics.
+Transformations for these loops are marked as `risky`, and thus will only
+be converted if the minimum required confidence level is set to `risky`.
+
+.. code-block:: c++
+
+ int arr[10][20];
+ int l = 5;
+
+ for (int j = 0; j < 20; ++j)
+ int k = arr[l][j] + l; // using l outside arr[l] is considered risky
+
+ for (int i = 0; i < obj.getVector().size(); ++i)
+ obj.foo(10); // using 'obj' is considered risky
+
+See
+:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
+for an example of an incorrect transformation when the minimum required confidence
+level is set to `risky`.
+
+reasonable (Default)
+^^^^^^^^^^^^^^^^^^^^
+
+If a loop calls ``.end()`` or ``.size()`` after each iteration, the
+transformation for that loop is marked as `reasonable`, and thus will
+be converted if the required confidence level is set to `reasonable`
+(default) or lower.
+
+.. code-block:: c++
+
+ // using size() is considered reasonable
+ for (int i = 0; i < container.size(); ++i)
+ cout << container[i];
+
+safe
+^^^^
+
+Any other loops that do not match the above criteria to be marked as
+`risky` or `reasonable` are marked `safe`, and thus will be converted
+if the required confidence level is set to `safe` or lower.
+
+.. code-block:: c++
+
+ int arr[] = {1,2,3};
+
+ for (int i = 0; i < 3; ++i)
+ cout << arr[i];
+
+Example
+-------
+
+Original:
+
+.. code-block:: c++
+
+ const int N = 5;
+ int arr[] = {1,2,3,4,5};
+ vector<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ v.push_back(3);
+
+ // safe conversion
+ for (int i = 0; i < N; ++i)
+ cout << arr[i];
+
+ // reasonable conversion
+ for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
+ cout << *it;
+
+ // reasonable conversion
+ for (int i = 0; i < v.size(); ++i)
+ cout << v[i];
+
+After applying the check with minimum confidence level set to `reasonable` (default):
+
+.. code-block:: c++
+
+ const int N = 5;
+ int arr[] = {1,2,3,4,5};
+ vector<int> v;
+ v.push_back(1);
+ v.push_back(2);
+ v.push_back(3);
+
+ // safe conversion
+ for (auto & elem : arr)
+ cout << elem;
+
+ // reasonable conversion
+ for (auto & elem : v)
+ cout << elem;
+
+ // reasonable conversion
+ for (auto & elem : v)
+ cout << elem;
+
+Limitations
+-----------
+
+There are certain situations where the tool may erroneously perform
+transformations that remove information and change semantics. Users of the tool
+should be aware of the behaviour and limitations of the check outlined by
+the cases below.
+
+Comments inside loop headers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Comments inside the original loop header are ignored and deleted when
+transformed.
+
+.. code-block:: c++
+
+ for (int i = 0; i < N; /* This will be deleted */ ++i) { }
+
+Range-based loops evaluate end() only once
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The C++11 range-based for loop calls ``.end()`` only once during the
+initialization of the loop. If in the original loop ``.end()`` is called after
+each iteration the semantics of the transformed loop may differ.
+
+.. code-block:: c++
+
+ // The following is semantically equivalent to the C++11 range-based for loop,
+ // therefore the semantics of the header will not change.
+ for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
+
+ // Instead of calling .end() after each iteration, this loop will be
+ // transformed to call .end() only once during the initialization of the loop,
+ // which may affect semantics.
+ for (iterator it = container.begin(); it != container.end(); ++it) { }
+
+.. _IncorrectRiskyTransformation:
+
+As explained above, calling member functions of the container in the body
+of the loop is considered `risky`. If the called member function modifies the
+container the semantics of the converted loop will differ due to ``.end()``
+being called only once.
+
+.. code-block:: c++
+
+ bool flag = false;
+ for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
+ // Add a copy of the first element to the end of the vector.
+ if (!flag) {
+ // This line makes this transformation 'risky'.
+ vec.push_back(*it);
+ flag = true;
+ }
+ cout << *it;
+ }
+
+The original code above prints out the contents of the container including the
+newly added element while the converted loop, shown below, will only print the
+original contents and not the newly added element.
+
+.. code-block:: c++
+
+ bool flag = false;
+ for (auto & elem : vec) {
+ // Add a copy of the first element to the end of the vector.
+ if (!flag) {
+ // This line makes this transformation 'risky'
+ vec.push_back(elem);
+ flag = true;
+ }
+ cout << elem;
+ }
+
+Semantics will also be affected if ``.end()`` has side effects. For example, in
+the case where calls to ``.end()`` are logged the semantics will change in the
+transformed loop if ``.end()`` was originally called after each iteration.
+
+.. code-block:: c++
+
+ iterator end() {
+ num_of_end_calls++;
+ return container.end();
+ }
+
+Overloaded operator->() with side effects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Similarly, if ``operator->()`` was overloaded to have side effects, such as
+logging, the semantics will change. If the iterator's ``operator->()`` was used
+in the original loop it will be replaced with ``<container element>.<member>``
+instead due to the implicit dereference as part of the range-based for loop.
+Therefore any side effect of the overloaded ``operator->()`` will no longer be
+performed.
+
+.. code-block:: c++
+
+ for (iterator it = c.begin(); it != c.end(); ++it) {
+ it->func(); // Using operator->()
+ }
+ // Will be transformed to:
+ for (auto & elem : c) {
+ elem.func(); // No longer using operator->()
+ }
+
+Pointers and references to containers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+While most of the check's risk analysis is dedicated to determining whether
+the iterator or container was modified within the loop, it is possible to
+circumvent the analysis by accessing and modifying the container through a
+pointer or reference.
+
+If the container were directly used instead of using the pointer or reference
+the following transformation would have only been applied at the `risky`
+level since calling a member function of the container is considered `risky`.
+The check cannot identify expressions associated with the container that are
+different than the one used in the loop header, therefore the transformation
+below ends up being performed at the `safe` level.
+
+.. code-block:: c++
+
+ vector<int> vec;
+
+ vector<int> *ptr = &vec;
+ vector<int> &ref = vec;
+
+ for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
+ if (!flag) {
+ // Accessing and modifying the container is considered risky, but the risk
+ // level is not raised here.
+ ptr->push_back(*it);
+ ref.push_back(*it);
+ flag = true;
+ }
+ }
--- /dev/null
+.. title:: clang-tidy - modernize-make-shared
+
+modernize-make-shared
+=====================
+
+This check finds the creation of ``std::shared_ptr`` objects by explicitly
+calling the constructor and a ``new`` expression, and replaces it with a call
+to ``std::make_shared``.
+
+.. code-block:: c++
+
+ auto my_ptr = std::shared_ptr<MyPair>(new MyPair(1, 2));
+
+ // becomes
+
+ auto my_ptr = std::make_shared<MyPair>(1, 2);
+
+This check also finds calls to ``std::shared_ptr::reset()`` with a ``new``
+expression, and replaces it with a call to ``std::make_shared``.
+
+.. code-block:: c++
+
+ my_ptr.reset(new MyPair(1, 2));
+
+ // becomes
+
+ my_ptr = std::make_shared<MyPair>(1, 2);
+
+Options
+-------
+
+.. option:: MakeSmartPtrFunction
+
+ A string specifying the name of make-shared-ptr function. Default is
+ `std::make_shared`.
+
+.. option:: MakeSmartPtrFunctionHeader
+
+ A string specifying the corresponding header of make-shared-ptr function.
+ Default is `memory`.
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - modernize-make-unique
+
+modernize-make-unique
+=====================
+
+This check finds the creation of ``std::unique_ptr`` objects by explicitly
+calling the constructor and a ``new`` expression, and replaces it with a call
+to ``std::make_unique``, introduced in C++14.
+
+.. code-block:: c++
+
+ auto my_ptr = std::unique_ptr<MyPair>(new MyPair(1, 2));
+
+ // becomes
+
+ auto my_ptr = std::make_unique<MyPair>(1, 2);
+
+This check also finds calls to ``std::unique_ptr::reset()`` with a ``new``
+expression, and replaces it with a call to ``std::make_unique``.
+
+.. code-block:: c++
+
+ my_ptr.reset(new MyPair(1, 2));
+
+ // becomes
+
+ my_ptr = std::make_unique<MyPair>(1, 2);
+
+Options
+-------
+
+.. option:: MakeSmartPtrFunction
+
+ A string specifying the name of make-unique-ptr function. Default is
+ `std::make_unique`.
+
+.. option:: MakeSmartPtrFunctionHeader
+
+ A string specifying the corresponding header of make-unique-ptr function.
+ Default is `memory`.
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - modernize-pass-by-value
+
+modernize-pass-by-value
+=======================
+
+With move semantics added to the language and the standard library updated with
+move constructors added for many types it is now interesting to take an
+argument directly by value, instead of by const-reference, and then copy. This
+check allows the compiler to take care of choosing the best way to construct
+the copy.
+
+The transformation is usually beneficial when the calling code passes an
+*rvalue* and assumes the move construction is a cheap operation. This short
+example illustrates how the construction of the value happens:
+
+ .. code-block:: c++
+
+ void foo(std::string s);
+ std::string get_str();
+
+ void f(const std::string &str) {
+ foo(str); // lvalue -> copy construction
+ foo(get_str()); // prvalue -> move construction
+ }
+
+.. note::
+
+ Currently, only constructors are transformed to make use of pass-by-value.
+ Contributions that handle other situations are welcome!
+
+
+Pass-by-value in constructors
+-----------------------------
+
+Replaces the uses of const-references constructor parameters that are copied
+into class fields. The parameter is then moved with `std::move()`.
+
+Since ``std::move()`` is a library function declared in `<utility>` it may be
+necessary to add this include. The check will add the include directive when
+necessary.
+
+ .. code-block:: c++
+
+ #include <string>
+
+ class Foo {
+ public:
+ - Foo(const std::string &Copied, const std::string &ReadOnly)
+ - : Copied(Copied), ReadOnly(ReadOnly)
+ + Foo(std::string Copied, const std::string &ReadOnly)
+ + : Copied(std::move(Copied)), ReadOnly(ReadOnly)
+ {}
+
+ private:
+ std::string Copied;
+ const std::string &ReadOnly;
+ };
+
+ std::string get_cwd();
+
+ void f(const std::string &Path) {
+ // The parameter corresponding to 'get_cwd()' is move-constructed. By
+ // using pass-by-value in the Foo constructor we managed to avoid a
+ // copy-construction.
+ Foo foo(get_cwd(), Path);
+ }
+
+
+If the parameter is used more than once no transformation is performed since
+moved objects have an undefined state. It means the following code will be left
+untouched:
+
+.. code-block:: c++
+
+ #include <string>
+
+ void pass(const std::string &S);
+
+ struct Foo {
+ Foo(const std::string &S) : Str(S) {
+ pass(S);
+ }
+
+ std::string Str;
+ };
+
+
+Known limitations
+^^^^^^^^^^^^^^^^^
+
+A situation where the generated code can be wrong is when the object referenced
+is modified before the assignment in the init-list through a "hidden" reference.
+
+Example:
+
+.. code-block:: c++
+
+ std::string s("foo");
+
+ struct Base {
+ Base() {
+ s = "bar";
+ }
+ };
+
+ struct Derived : Base {
+ - Derived(const std::string &S) : Field(S)
+ + Derived(std::string S) : Field(std::move(S))
+ { }
+
+ std::string Field;
+ };
+
+ void f() {
+ - Derived d(s); // d.Field holds "bar"
+ + Derived d(s); // d.Field holds "foo"
+ }
+
+
+Note about delayed template parsing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When delayed template parsing is enabled, constructors part of templated
+contexts; templated constructors, constructors in class templates, constructors
+of inner classes of template classes, etc., are not transformed. Delayed
+template parsing is enabled by default on Windows as a Microsoft extension:
+`Clang Compiler User’s Manual - Microsoft extensions`_.
+
+Delayed template parsing can be enabled using the `-fdelayed-template-parsing`
+flag and disabled using `-fno-delayed-template-parsing`.
+
+Example:
+
+.. code-block:: c++
+
+ template <typename T> class C {
+ std::string S;
+
+ public:
+ = // using -fdelayed-template-parsing (default on Windows)
+ = C(const std::string &S) : S(S) {}
+
+ + // using -fno-delayed-template-parsing (default on non-Windows systems)
+ + C(std::string S) : S(std::move(S)) {}
+ };
+
+.. _Clang Compiler User’s Manual - Microsoft extensions: http://clang.llvm.org/docs/UsersManual.html#microsoft-extensions
+
+.. seealso::
+
+ For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_.
+
+ .. _Want Speed? Pass by Value: https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
+
+.. option:: ValuesOnly
+
+ When non-zero, the check only warns about copied parameters that are already
+ passed by value. Default is `0`.
--- /dev/null
+.. title:: clang-tidy - modernize-raw-string-literal
+
+modernize-raw-string-literal
+============================
+
+This check selectively replaces string literals containing escaped characters
+with raw string literals.
+
+Example:
+
+.. code-blocK:: c++
+
+ const char *const Quotes{"embedded \"quotes\""};
+ const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
+ const char *const SingleLine{"Single line.\n"};
+ const char *const TrailingSpace{"Look here -> \n"};
+ const char *const Tab{"One\tTwo\n"};
+ const char *const Bell{"Hello!\a And welcome!"};
+ const char *const Path{"C:\\Program Files\\Vendor\\Application.exe"};
+ const char *const RegEx{"\\w\\([a-z]\\)"};
+
+becomes
+
+.. code-block:: c++
+
+ const char *const Quotes{R"(embedded "quotes")"};
+ const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
+ const char *const SingleLine{"Single line.\n"};
+ const char *const TrailingSpace{"Look here -> \n"};
+ const char *const Tab{"One\tTwo\n"};
+ const char *const Bell{"Hello!\a And welcome!"};
+ const char *const Path{R"(C:\Program Files\Vendor\Application.exe)"};
+ const char *const RegEx{R"(\w\([a-z]\))"};
+
+The presence of any of the following escapes can cause the string to be
+converted to a raw string literal: ``\\``, ``\'``, ``\"``, ``\?``,
+and octal or hexadecimal escapes for printable ASCII characters.
+
+A string literal containing only escaped newlines is a common way of
+writing lines of text output. Introducing physical newlines with raw
+string literals in this case is likely to impede readability. These
+string literals are left unchanged.
+
+An escaped horizontal tab, form feed, or vertical tab prevents the string
+literal from being converted. The presence of a horizontal tab, form feed or
+vertical tab in source code is not visually obvious.
--- /dev/null
+.. title:: clang-tidy - modernize-redundant-void-arg
+
+modernize-redundant-void-arg
+============================
+
+Find and remove redundant ``void`` argument lists.
+
+Examples:
+ =================================== ===========================
+ Initial code Code with applied fixes
+ =================================== ===========================
+ ``int f(void);`` ``int f();``
+ ``int (*f(void))(void);`` ``int (*f())();``
+ ``typedef int (*f_t(void))(void);`` ``typedef int (*f_t())();``
+ ``void (C::*p)(void);`` ``void (C::*p)();``
+ ``C::C(void) {}`` ``C::C() {}``
+ ``C::~C(void) {}`` ``C::~C() {}``
+ =================================== ===========================
--- /dev/null
+.. title:: clang-tidy - modernize-replace-auto-ptr
+
+modernize-replace-auto-ptr
+==========================
+
+This check replaces the uses of the deprecated class ``std::auto_ptr`` by
+``std::unique_ptr`` (introduced in C++11). The transfer of ownership, done
+by the copy-constructor and the assignment operator, is changed to match
+``std::unique_ptr`` usage by using explicit calls to ``std::move()``.
+
+Migration example:
+
+.. code-block:: c++
+
+ -void take_ownership_fn(std::auto_ptr<int> int_ptr);
+ +void take_ownership_fn(std::unique_ptr<int> int_ptr);
+
+ void f(int x) {
+ - std::auto_ptr<int> a(new int(x));
+ - std::auto_ptr<int> b;
+ + std::unique_ptr<int> a(new int(x));
+ + std::unique_ptr<int> b;
+
+ - b = a;
+ - take_ownership_fn(b);
+ + b = std::move(a);
+ + take_ownership_fn(std::move(b));
+ }
+
+Since ``std::move()`` is a library function declared in ``<utility>`` it may be
+necessary to add this include. The check will add the include directive when
+necessary.
+
+Known Limitations
+-----------------
+* If headers modification is not activated or if a header is not allowed to be
+ changed this check will produce broken code (compilation error), where the
+ headers' code will stay unchanged while the code using them will be changed.
+
+* Client code that declares a reference to an ``std::auto_ptr`` coming from
+ code that can't be migrated (such as a header coming from a 3\ :sup:`rd`
+ party library) will produce a compilation error after migration. This is
+ because the type of the reference will be changed to ``std::unique_ptr`` but
+ the type returned by the library won't change, binding a reference to
+ ``std::unique_ptr`` from an ``std::auto_ptr``. This pattern doesn't make much
+ sense and usually ``std::auto_ptr`` are stored by value (otherwise what is
+ the point in using them instead of a reference or a pointer?).
+
+ .. code-block:: c++
+
+ // <3rd-party header...>
+ std::auto_ptr<int> get_value();
+ const std::auto_ptr<int> & get_ref();
+
+ // <calling code (with migration)...>
+ -std::auto_ptr<int> a(get_value());
+ +std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr
+
+ -const std::auto_ptr<int> & p = get_ptr();
+ +const std::unique_ptr<int> & p = get_ptr(); // won't compile
+
+* Non-instantiated templates aren't modified.
+
+ .. code-block:: c++
+
+ template <typename X>
+ void f() {
+ std::auto_ptr<X> p;
+ }
+
+ // only 'f<int>()' (or similar) will trigger the replacement.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - modernize-replace-random-shuffle
+
+modernize-replace-random-shuffle
+================================
+
+This check will find occurrences of ``std::random_shuffle`` and replace it with ``std::shuffle``. In C++17 ``std::random_shuffle`` will no longer be available and thus we need to replace it.
+
+Below are two examples of what kind of occurrences will be found and two examples of what it will be replaced with.
+
+.. code-block:: c++
+
+ std::vector<int> v;
+
+ // First example
+ std::random_shuffle(vec.begin(), vec.end());
+
+ // Second example
+ std::random_shuffle(vec.begin(), vec.end(), randomFun);
+
+Both of these examples will be replaced with:
+
+.. code-block:: c++
+
+ std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+The second example will also receive a warning that ``randomFunc`` is no longer supported in the same way as before so if the user wants the same functionality, the user will need to change the implementation of the ``randomFunc``.
+
+One thing to be aware of here is that ``std::random_device`` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere.
--- /dev/null
+.. title:: clang-tidy - modernize-return-braced-init-list
+
+modernize-return-braced-init-list
+=================================
+
+Replaces explicit calls to the constructor in a return with a braced
+initializer list. This way the return type is not needlessly duplicated in the
+function definition and the return statement.
+
+.. code:: c++
+
+ Foo bar() {
+ Baz baz;
+ return Foo(baz);
+ }
+
+ // transforms to:
+
+ Foo bar() {
+ Baz baz;
+ return {baz};
+ }
--- /dev/null
+.. title:: clang-tidy - modernize-shrink-to-fit
+
+modernize-shrink-to-fit
+=======================
+
+
+Replace copy and swap tricks on shrinkable containers with the
+``shrink_to_fit()`` method call.
+
+The ``shrink_to_fit()`` method is more readable and more effective than
+the copy and swap trick to reduce the capacity of a shrinkable container.
+Note that, the ``shrink_to_fit()`` method is only available in C++11 and up.
--- /dev/null
+.. title:: clang-tidy - modernize-unary-static-assert
+
+modernize-unary-static-assert
+=============================
+
+The check diagnoses any ``static_assert`` declaration with an empty string literal
+and provides a fix-it to replace the declaration with a single-argument ``static_assert`` declaration.
+
+The check is only applicable for C++17 and later code.
+
+The following code:
+
+.. code-block:: c++
+
+ void f_textless(int a) {
+ static_assert(sizeof(a) <= 10, "");
+ }
+
+is replaced by:
+
+.. code-block:: c++
+
+ void f_textless(int a) {
+ static_assert(sizeof(a) <= 10);
+ }
--- /dev/null
+.. title:: clang-tidy - modernize-use-auto
+
+modernize-use-auto
+==================
+
+This check is responsible for using the ``auto`` type specifier for variable
+declarations to *improve code readability and maintainability*. For example:
+
+.. code-block:: c++
+
+ std::vector<int>::iterator I = my_container.begin();
+
+ // transforms to:
+
+ auto I = my_container.begin();
+
+The ``auto`` type specifier will only be introduced in situations where the
+variable type matches the type of the initializer expression. In other words
+``auto`` should deduce the same type that was originally spelled in the source.
+However, not every situation should be transformed:
+
+.. code-block:: c++
+
+ int val = 42;
+ InfoStruct &I = SomeObject.getInfo();
+
+ // Should not become:
+
+ auto val = 42;
+ auto &I = SomeObject.getInfo();
+
+In this example using ``auto`` for builtins doesn't improve readability. In
+other situations it makes the code less self-documenting impairing readability
+and maintainability. As a result, ``auto`` is used only introduced in specific
+situations described below.
+
+Iterators
+---------
+
+Iterator type specifiers tend to be long and used frequently, especially in
+loop constructs. Since the functions generating iterators have a common format,
+the type specifier can be replaced without obscuring the meaning of code while
+improving readability and maintainability.
+
+.. code-block:: c++
+
+ for (std::vector<int>::iterator I = my_container.begin(),
+ E = my_container.end();
+ I != E; ++I) {
+ }
+
+ // becomes
+
+ for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
+ }
+
+The check will only replace iterator type-specifiers when all of the following
+conditions are satisfied:
+
+* The iterator is for one of the standard container in ``std`` namespace:
+
+ * ``array``
+ * ``deque``
+ * ``forward_list``
+ * ``list``
+ * ``vector``
+ * ``map``
+ * ``multimap``
+ * ``set``
+ * ``multiset``
+ * ``unordered_map``
+ * ``unordered_multimap``
+ * ``unordered_set``
+ * ``unordered_multiset``
+ * ``queue``
+ * ``priority_queue``
+ * ``stack``
+
+* The iterator is one of the possible iterator types for standard containers:
+
+ * ``iterator``
+ * ``reverse_iterator``
+ * ``const_iterator``
+ * ``const_reverse_iterator``
+
+* In addition to using iterator types directly, typedefs or other ways of
+ referring to those types are also allowed. However, implementation-specific
+ types for which a type like ``std::vector<int>::iterator`` is itself a
+ typedef will not be transformed. Consider the following examples:
+
+.. code-block:: c++
+
+ // The following direct uses of iterator types will be transformed.
+ std::vector<int>::iterator I = MyVec.begin();
+ {
+ using namespace std;
+ list<int>::iterator I = MyList.begin();
+ }
+
+ // The type specifier for J would transform to auto since it's a typedef
+ // to a standard iterator type.
+ typedef std::map<int, std::string>::const_iterator map_iterator;
+ map_iterator J = MyMap.begin();
+
+ // The following implementation-specific iterator type for which
+ // std::vector<int>::iterator could be a typedef would not be transformed.
+ __gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
+
+* The initializer for the variable being declared is not a braced initializer
+ list. Otherwise, use of ``auto`` would cause the type of the variable to be
+ deduced as ``std::initializer_list``.
+
+New expressions
+---------------
+
+Frequently, when a pointer is declared and initialized with ``new``, the
+pointee type is written twice: in the declaration type and in the
+``new`` expression. In this cases, the declaration type can be replaced with
+``auto`` improving readability and maintainability.
+
+.. code-block:: c++
+
+ TypeName *my_pointer = new TypeName(my_param);
+
+ // becomes
+
+ auto *my_pointer = new TypeName(my_param);
+
+The check will also replace the declaration type in multiple declarations, if
+the following conditions are satisfied:
+
+* All declared variables have the same type (i.e. all of them are pointers to
+ the same type).
+* All declared variables are initialized with a ``new`` expression.
+* The types of all the new expressions are the same than the pointee of the
+ declaration type.
+
+.. code-block:: c++
+
+ TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+ // becomes
+
+ auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+Cast expressions
+----------------
+
+Frequently, when a variable is declared and initialized with a cast, the
+variable type is written twice: in the declaration type and in the
+cast expression. In this cases, the declaration type can be replaced with
+``auto`` improving readability and maintainability.
+
+.. code-block:: c++
+
+ TypeName *my_pointer = static_cast<TypeName>(my_param);
+
+ // becomes
+
+ auto *my_pointer = static_cast<TypeName>(my_param);
+
+The check handles ``static_cast``, ``dynamic_cast``, ``const_cast``,
+``reinterpret_cast``, functional casts, C-style casts and function templates
+that behave as casts, such as ``llvm::dyn_cast``, ``boost::lexical_cast`` and
+``gsl::narrow_cast``. Calls to function templates are considered to behave as
+casts if the first template argument is explicit and is a type, and the function
+returns that type, or a pointer or reference to it.
+
+Known Limitations
+-----------------
+
+* If the initializer is an explicit conversion constructor, the check will not
+ replace the type specifier even though it would be safe to do so.
+
+* User-defined iterators are not handled at this time.
+
+Options
+-------
+
+.. option:: RemoveStars
+
+ If the option is set to non-zero (default is `0`), the check will remove
+ stars from the non-typedef pointer types when replacing type names with
+ ``auto``. Otherwise, the check will leave stars. For example:
+
+.. code-block:: c++
+
+ TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+ // RemoveStars = 0
+
+ auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+ // RemoveStars = 1
+
+ auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;
--- /dev/null
+.. title:: clang-tidy - modernize-use-bool-literals
+
+modernize-use-bool-literals
+===========================
+
+Finds integer literals which are cast to ``bool``.
+
+.. code-block:: c++
+
+ bool p = 1;
+ bool f = static_cast<bool>(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;
--- /dev/null
+.. title:: clang-tidy - modernize-use-default-member-init
+
+modernize-use-default-member-init
+=================================
+
+This check converts a default constructor's member initializers into the new
+default member initializers in C++11. Other member initializers that match the
+default member initializer are removed. This can reduce repeated code or allow
+use of '= default'.
+
+.. code-block:: c++
+
+ struct A {
+ A() : i(5), j(10.0) {}
+ A(int i) : i(i), j(10.0) {}
+ int i;
+ double j;
+ };
+
+ // becomes
+
+ struct A {
+ A() {}
+ A(int i) : i(i) {}
+ int i{5};
+ double j{10.0};
+ };
+
+.. note::
+ Only converts member initializers for built-in types, enums, and pointers.
+ The `readability-redundant-member-init` check will remove redundant member
+ initializers for classes.
+
+Options
+-------
+
+.. option:: UseAssignment
+
+ If this option is set to non-zero (default is `0`), the check will initialise
+ members with an assignment. For example:
+
+.. code-block:: c++
+
+ struct A {
+ A() {}
+ A(int i) : i(i) {}
+ int i = 5;
+ double j = 10.0;
+ };
+
+.. option:: IgnoreMacros
+
+ If this option is set to non-zero (default is `1`), the check will not warn
+ about members declared inside macros.
--- /dev/null
+:orphan:
+
+.. title:: clang-tidy - modernize-use-default
+.. meta::
+ :http-equiv=refresh: 5;URL=modernize-use-equals-default.html
+
+modernize-use-default
+=====================
+
+This check has been renamed to
+`modernize-use-equals-default <modernize-use-equals-default.html>`_.
--- /dev/null
+.. title:: clang-tidy - modernize-use-emplace
+
+modernize-use-emplace
+=====================
+
+The check flags insertions to an STL-style container done by calling the
+``push_back`` method with an explicitly-constructed temporary of the container
+element type. In this case, the corresponding ``emplace_back`` method
+results in less verbose and potentially more efficient code.
+Right now the check doesn't support ``push_front`` and ``insert``.
+It also doesn't support ``insert`` functions for associative containers
+because replacing ``insert`` with ``emplace`` may result in
+`speed regression <http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html>`_, but it might get support with some addition flag in the future.
+
+By default only ``std::vector``, ``std::deque``, ``std::list`` are considered.
+This list can be modified using the :option:`ContainersWithPushBack` option.
+
+Before:
+
+.. code-block:: c++
+
+ std::vector<MyClass> v;
+ v.push_back(MyClass(21, 37));
+
+ std::vector<std::pair<int, int>> w;
+
+ w.push_back(std::pair<int, int>(21, 37));
+ w.push_back(std::make_pair(21L, 37L));
+
+After:
+
+.. code-block:: c++
+
+ std::vector<MyClass> v;
+ v.emplace_back(21, 37);
+
+ std::vector<std::pair<int, int>> w;
+ w.emplace_back(21, 37);
+ w.emplace_back(21L, 37L);
+
+By default, the check is able to remove unnecessary ``std::make_pair`` and
+``std::make_tuple`` calls from ``push_back`` calls on containers of
+``std::pair`` and ``std::tuple``. Custom tuple-like types can be modified by
+the :option:`TupleTypes` option; custom make functions can be modified by the
+:option:`TupleMakeFunctions` option.
+
+The other situation is when we pass arguments that will be converted to a type
+inside a container.
+
+Before:
+
+.. code-block:: c++
+
+ std::vector<boost::optional<std::string> > v;
+ v.push_back("abc");
+
+After:
+
+.. code-block:: c++
+
+ std::vector<boost::optional<std::string> > v;
+ v.emplace_back("abc");
+
+
+In some cases the transformation would be valid, but the code wouldn't be
+exception safe. In this case the calls of ``push_back`` won't be replaced.
+
+.. code-block:: c++
+
+ std::vector<std::unique_ptr<int>> v;
+ v.push_back(std::unique_ptr<int>(new int(0)));
+ auto *ptr = new int(1);
+ v.push_back(std::unique_ptr<int>(ptr));
+
+This is because replacing it with ``emplace_back`` could cause a leak of this
+pointer if ``emplace_back`` would throw exception before emplacement (e.g. not
+enough memory to add a new element).
+
+For more info read item 42 - "Consider emplacement instead of insertion." of
+Scott Meyers "Effective Modern C++".
+
+The default smart pointers that are considered are ``std::unique_ptr``,
+``std::shared_ptr``, ``std::auto_ptr``. To specify other smart pointers or
+other classes use the :option:`SmartPointers` option.
+
+
+Check also doesn't fire if any argument of the constructor call would be:
+
+ - a bit-field (bit-fields can't bind to rvalue/universal reference)
+
+ - a ``new`` expression (to avoid leak)
+
+ - if the argument would be converted via derived-to-base cast.
+
+This check requires C++11 or higher to run.
+
+Options
+-------
+
+.. option:: ContainersWithPushBack
+
+ Semicolon-separated list of class names of custom containers that support
+ ``push_back``.
+
+.. option:: SmartPointers
+
+ Semicolon-separated list of class names of custom smart pointers.
+
+.. option:: TupleTypes
+
+ Semicolon-separated list of ``std::tuple``-like class names.
+
+.. option:: TupleMakeFunctions
+
+ Semicolon-separated list of ``std::make_tuple``-like function names. Those
+ function calls will be removed from ``push_back`` calls and turned into
+ ``emplace_back``.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+ std::vector<MyTuple<int, bool, char>> x;
+ x.push_back(MakeMyTuple(1, false, 'x'));
+
+transforms to:
+
+.. code-block:: c++
+
+ std::vector<MyTuple<int, bool, char>> x;
+ x.emplace_back(1, false, 'x');
+
+when :option:`TupleTypes` is set to ``MyTuple`` and :option:`TupleMakeFunctions`
+is set to ``MakeMyTuple``.
--- /dev/null
+.. title:: clang-tidy - modernize-use-equals-default
+
+modernize-use-equals-default
+============================
+
+This check replaces default bodies of special member functions with ``=
+default;``. The explicitly defaulted function declarations enable more
+opportunities in optimization, because the compiler might treat explicitly
+defaulted functions as trivial.
+
+.. code-block:: c++
+
+ struct A {
+ A() {}
+ ~A();
+ };
+ A::~A() {}
+
+ // becomes
+
+ struct A {
+ A() = default;
+ ~A();
+ };
+ A::~A() = default;
+
+.. note::
+ Move-constructor and move-assignment operator are not supported yet.
--- /dev/null
+.. title:: clang-tidy - modernize-use-equals-delete
+
+modernize-use-equals-delete
+===========================
+
+This check marks unimplemented private special member functions with ``= delete``.
+To avoid false-positives, this check only applies in a translation unit that has
+all other member functions implemented.
+
+.. code-block:: c++
+
+ struct A {
+ private:
+ A(const A&);
+ A& operator=(const A&);
+ };
+
+ // becomes
+
+ struct A {
+ private:
+ A(const A&) = delete;
+ A& operator=(const A&) = delete;
+ };
+
--- /dev/null
+.. title:: clang-tidy - modernize-use-noexcept
+
+modernize-use-noexcept
+======================
+
+This check replaces deprecated dynamic exception specifications with
+the appropriate noexcept specification (introduced in C++11). By
+default this check will replace ``throw()`` with ``noexcept``,
+and ``throw(<exception>[,...])`` or ``throw(...)`` with
+``noexcept(false)``.
+
+Example
+-------
+
+.. code-block:: c++
+
+ void foo() throw();
+ void bar() throw(int) {}
+
+transforms to:
+
+.. code-block:: c++
+
+ void foo() noexcept;
+ void bar() noexcept(false) {}
+
+Options
+-------
+
+.. option:: ReplacementString
+
+Users can use :option:`ReplacementString` to specify a macro to use
+instead of ``noexcept``. This is useful when maintaining source code
+that uses custom exception specification marking other than
+``noexcept``. Fix-it hints will only be generated for non-throwing
+specifications.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+ void bar() throw(int);
+ void foo() throw();
+
+transforms to:
+
+.. code-block:: c++
+
+ void bar() throw(int); // No fix-it generated.
+ void foo() NOEXCEPT;
+
+if the :option:`ReplacementString` option is set to `NOEXCEPT`.
+
+.. option:: UseNoexceptFalse
+
+Enabled by default, disabling will generate fix-it hints that remove
+throwing dynamic exception specs, e.g., ``throw(<something>)``,
+completely without providing a replacement text, except for
+destructors and delete operators that are ``noexcept(true)`` by
+default.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+ void foo() throw(int) {}
+
+ struct bar {
+ void foobar() throw(int);
+ void operator delete(void *ptr) throw(int);
+ void operator delete[](void *ptr) throw(int);
+ ~bar() throw(int);
+ }
+
+transforms to:
+
+.. code-block:: c++
+
+ void foo() {}
+
+ struct bar {
+ void foobar();
+ void operator delete(void *ptr) noexcept(false);
+ void operator delete[](void *ptr) noexcept(false);
+ ~bar() noexcept(false);
+ }
+
+if the :option:`UseNoexceptFalse` option is set to `0`.
--- /dev/null
+.. title:: clang-tidy - modernize-use-nullptr
+
+modernize-use-nullptr
+=====================
+
+The check converts the usage of null pointer constants (eg. ``NULL``, ``0``)
+to use the new C++11 ``nullptr`` keyword.
+
+Example
+-------
+
+.. code-block:: c++
+
+ void assignment() {
+ char *a = NULL;
+ char *b = 0;
+ char c = 0;
+ }
+
+ int *ret_ptr() {
+ return 0;
+ }
+
+
+transforms to:
+
+.. code-block:: c++
+
+ void assignment() {
+ char *a = nullptr;
+ char *b = nullptr;
+ char c = 0;
+ }
+
+ int *ret_ptr() {
+ return nullptr;
+ }
+
+Options
+-------
+
+.. option:: NullMacros
+
+ Comma-separated list of macro names that will be transformed along with
+ ``NULL``. By default this check will only replace the ``NULL`` macro and will
+ skip any similar user-defined macros.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+ #define MY_NULL (void*)0
+ void assignment() {
+ void *p = MY_NULL;
+ }
+
+transforms to:
+
+.. code-block:: c++
+
+ #define MY_NULL NULL
+ void assignment() {
+ int *p = nullptr;
+ }
+
+if the :option:`NullMacros` option is set to ``MY_NULL``.
--- /dev/null
+.. title:: clang-tidy - modernize-use-override
+
+modernize-use-override
+======================
+
+
+Use C++11's ``override`` and remove ``virtual`` where applicable.
--- /dev/null
+.. title:: clang-tidy - modernize-use-transparent-functors
+
+modernize-use-transparent-functors
+==================================
+
+Prefer transparent functors to non-transparent ones. When using transparent
+functors, the type does not need to be repeated. The code is easier to read,
+maintain and less prone to errors. It is not possible to introduce unwanted
+conversions.
+
+ .. code-block:: c++
+
+ // Non-transparent functor
+ std::map<int, std::string, std::greater<int>> s;
+
+ // Transparent functor.
+ std::map<int, std::string, std::greater<>> s;
+
+ // Non-transparent functor
+ using MyFunctor = std::less<MyType>;
+
+It is not always a safe transformation though. The following case will be
+untouched to preserve the semantics.
+
+ .. code-block:: c++
+
+ // Non-transparent functor
+ std::map<const char *, std::string, std::greater<std::string>> s;
+
+Options
+-------
+
+.. option:: SafeMode
+
+ If the option is set to non-zero, the check will not diagnose cases where
+ using a transparent functor cannot be guaranteed to produce identical results
+ as the original code. The default value for this option is `0`.
+
+This check requires using C++14 or higher to run.
--- /dev/null
+.. title:: clang-tidy - modernize-use-using
+
+modernize-use-using
+===================
+
+The check converts the usage of ``typedef`` with ``using`` keyword.
+
+Before:
+
+.. code-block:: c++
+
+ typedef int variable;
+
+ class Class{};
+ typedef void (Class::* MyPtrType)() const;
+
+After:
+
+.. code-block:: c++
+
+ using variable = int;
+
+ class Class{};
+ using MyPtrType = void (Class::*)() const;
+
+This check requires using C++11 or higher to run.
--- /dev/null
+.. title:: clang-tidy - mpi-buffer-deref
+
+mpi-buffer-deref
+================
+
+This check verifies if a buffer passed to an MPI (Message Passing Interface)
+function is sufficiently dereferenced. Buffers should be passed as a single
+pointer or array. As MPI function signatures specify ``void *`` for their buffer
+types, insufficiently dereferenced buffers can be passed, like for example as
+double pointers or multidimensional arrays, without a compiler warning emitted.
+
+Examples:
+
+.. code-block:: c++
+
+ // A double pointer is passed to the MPI function.
+ char *buf;
+ MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+ // A multidimensional array is passed to the MPI function.
+ short buf[1][1];
+ MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+
+ // A pointer to an array is passed to the MPI function.
+ short *buf[1];
+ MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
--- /dev/null
+.. 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-block:: 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);
--- /dev/null
+.. title:: clang-tidy - performance-faster-string-find
+
+performance-faster-string-find
+==============================
+
+Optimize calls to ``std::string::find()`` and friends when the needle passed is
+a single character string literal. The character literal overload is more
+efficient.
+
+Examples:
+
+.. code-block:: c++
+
+ str.find("A");
+
+ // becomes
+
+ str.find('A');
+
+Options
+-------
+
+.. option:: StringLikeClasses
+
+ Semicolon-separated list of names of string-like classes. By default only
+ ``std::basic_string`` is considered. The list of methods to consired is
+ fixed.
+
--- /dev/null
+.. title:: clang-tidy - performance-for-range-copy
+
+performance-for-range-copy
+==========================
+
+Finds C++11 for ranges where the loop variable is copied in each iteration but
+it would suffice to obtain it by const reference.
+
+The check is only applied to loop variables of types that are expensive to copy
+which means they are not trivially copyable or have a non-trivial copy
+constructor or destructor.
+
+To ensure that it is safe to replace the copy with a const reference the
+following heuristic is employed:
+
+1. The loop variable is const qualified.
+2. The loop variable is not const, but only const methods or operators are
+ invoked on it, or it is used as const reference or value argument in
+ constructors or function calls.
+
+Options
+-------
+
+.. option:: WarnOnAllAutoCopies
+
+ When non-zero, warns on any use of `auto` as the type of the range-based for
+ loop variable. Default is `0`.
--- /dev/null
+.. title:: clang-tidy - performance-implicit-cast-in-loop
+
+performance-implicit-cast-in-loop
+=================================
+
+This warning appears in a range-based loop with a loop variable of const ref
+type where the type of the variable does not match the one returned by the
+iterator. This means that an implicit cast has been added, which can for example
+result in expensive deep copies.
+
+Example:
+
+.. code-block:: c++
+
+ map<int, vector<string>> my_map;
+ for (const pair<int, vector<string>>& p : my_map) {}
+ // The iterator type is in fact pair<const int, vector<string>>, which means
+ // that the compiler added a cast, resulting in a copy of the vectors.
+
+The easiest solution is usually to use ``const auto&`` instead of writing the type
+manually.
--- /dev/null
+.. 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-block:: 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-block:: 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-block:: 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-block:: 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-block:: c++
+
+ void f(const std::string&) {}
+ std::string a("Foo"), b("Baz");
+ void g() {
+ f(std::string(a).append("Bar").append(b));
+ }
+
+Options
+-------
+
+.. option:: StrictMode
+
+ When zero, the check will only check the string usage in ``while``, ``for``
+ and ``for-range`` statements. Default is `0`.
--- /dev/null
+.. title:: clang-tidy - performance-inefficient-vector-operation
+
+performance-inefficient-vector-operation
+========================================
+
+Finds possible inefficient ``std::vector`` operations (e.g. ``push_back``,
+``emplace_back``) that may cause unnecessary memory reallocations.
+
+Currently, the check only detects following kinds of loops with a single
+statement body:
+
+* Counter-based for loops start with 0:
+
+.. code-block:: c++
+
+ std::vector<int> v;
+ for (int i = 0; i < n; ++i) {
+ v.push_back(n);
+ // This will trigger the warning since the push_back may cause multiple
+ // memory reallocations in v. This can be avoid by inserting a 'reserve(n)'
+ // statement before the for statement.
+ }
+
+
+* For-range loops like ``for (range-declaration : range_expression)``, the type
+ of ``range_expression`` can be ``std::vector``, ``std::array``,
+ ``std::deque``, ``std::set``, ``std::unordered_set``, ``std::map``,
+ ``std::unordered_set``:
+
+.. code-block:: c++
+
+ std::vector<int> data;
+ std::vector<int> v;
+
+ for (auto element : data) {
+ v.push_back(element);
+ // This will trigger the warning since the 'push_back' may cause multiple
+ // memory reallocations in v. This can be avoid by inserting a
+ // 'reserve(data.size())' statement before the for statement.
+ }
+
+
+Options
+-------
+
+.. option:: VectorLikeClasses
+
+ Semicolon-separated list of names of vector-like classes. By default only
+ ``::std::vector`` is considered.
--- /dev/null
+.. title:: clang-tidy - performance-type-promotion-in-math-fn
+
+performance-type-promotion-in-math-fn
+=====================================
+
+Finds calls to C math library functions (from ``math.h`` or, in C++, ``cmath``)
+with implicit ``float`` to ``double`` promotions.
+
+For example, warns on ``::sin(0.f)``, because this funciton's parameter is a
+double. You probably meant to call ``std::sin(0.f)`` (in C++), or ``sinf(0.f)``
+(in C).
+
+.. code-block:: c++
+
+ float a;
+ asin(a);
+
+ // becomes
+
+ float a;
+ std::asin(a);
--- /dev/null
+.. title:: clang-tidy - performance-unnecessary-copy-initialization
+
+performance-unnecessary-copy-initialization
+===========================================
+
+Finds local variable declarations that are initialized using the copy
+constructor of a non-trivially-copyable type but it would suffice to obtain a
+const reference.
+
+The check is only applied if it is safe to replace the copy by a const
+reference. This is the case when the variable is const qualified or when it is
+only used as a const, i.e. only const methods or operators are invoked on it, or
+it is used as const reference or value argument in constructors or function
+calls.
+
+Example:
+
+.. code-block:: c++
+
+ const string& constReference();
+ void Function() {
+ // The warning will suggest making this a const reference.
+ const string UnnecessaryCopy = constReference();
+ }
+
+ struct Foo {
+ const string& name() const;
+ };
+ void Function(const Foo& foo) {
+ // The warning will suggest making this a const reference.
+ string UnnecessaryCopy1 = foo.name();
+ UnnecessaryCopy1.find("bar");
+
+ // The warning will suggest making this a const reference.
+ string UnnecessaryCopy2 = UnnecessaryCopy1;
+ UnnecessaryCopy2.find("bar");
+ }
--- /dev/null
+.. title:: clang-tidy - performance-unnecessary-value-param
+
+performance-unnecessary-value-param
+===================================
+
+Flags value parameter declarations of expensive to copy types that are copied
+for each invocation but it would suffice to pass them by const reference.
+
+The check is only applied to parameters of types that are expensive to copy
+which means they are not trivially copyable or have a non-trivial copy
+constructor or destructor.
+
+To ensure that it is safe to replace the value parameter with a const reference
+the following heuristic is employed:
+
+1. the parameter is const qualified;
+2. the parameter is not const, but only const methods or operators are invoked
+ on it, or it is used as const reference or value argument in constructors or
+ function calls.
+
+Example:
+
+.. code-block:: c++
+
+ void f(const string Value) {
+ // The warning will suggest making Value a reference.
+ }
+
+ void g(ExpensiveToCopy Value) {
+ // The warning will suggest making Value a const reference.
+ Value.ConstMethd();
+ ExpensiveToCopy Copy(Value);
+ }
+
+If the parameter is not const, only copied or assigned once and has a
+non-trivial move-constructor or move-assignment operator respectively the check
+will suggest to move it.
+
+Example:
+
+.. code-block:: c++
+
+ void setValue(string Value) {
+ Field = Value;
+ }
+
+Will become:
+
+.. code-block:: c++
+
+ #include <utility>
+
+ void setValue(string Value) {
+ Field = std::move(Value);
+ }
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+ A string specifying which include-style is used, `llvm` or `google`. Default
+ is `llvm`.
--- /dev/null
+.. title:: clang-tidy - readability-avoid-const-params-in-decls
+
+readability-avoid-const-params-in-decls
+=======================================
+
+Checks whether a function declaration has parameters that are top level
+``const``.
+
+``const`` values in declarations do not affect the signature of a function, so
+they should not be put there.
+
+Examples:
+
+.. code-block:: c++
+
+ void f(const string); // Bad: const is top level.
+ void f(const string&); // Good: const is not top level.
--- /dev/null
+.. title:: clang-tidy - readability-braces-around-statements
+
+readability-braces-around-statements
+====================================
+
+`google-readability-braces-around-statements` redirects here as an alias for
+this check.
+
+Checks that bodies of ``if`` statements and loops (``for``, ``do while``, and
+``while``) are inside braces.
+
+Before:
+
+.. code-block:: c++
+
+ if (condition)
+ statement;
+
+After:
+
+.. code-block:: c++
+
+ if (condition) {
+ statement;
+ }
+
+Options
+-------
+
+.. option:: ShortStatementLines
+
+ Defines the minimal number of lines that the statement should have in order
+ to trigger this check.
+
+ The number of lines is counted from the end of condition or initial keyword
+ (``do``/``else``) until the last line of the inner statement. Default value
+ `0` means that braces will be added to all statements (not having them
+ already).
--- /dev/null
+.. title:: clang-tidy - readability-container-size-empty
+
+readability-container-size-empty
+================================
+
+
+Checks whether a call to the ``size()`` method can be replaced with a call to
+``empty()``.
+
+The emptiness of a container should be checked using the ``empty()`` method
+instead of the ``size()`` method. It is not guaranteed that ``size()`` is a
+constant-time function, and it is generally more efficient and also shows
+clearer intent to use ``empty()``. Furthermore some containers may implement
+the ``empty()`` method but not implement the ``size()`` method. Using
+``empty()`` whenever possible makes it easier to switch to another container in
+the future.
+
+The check issues warning if a container has ``size()`` and ``empty()`` methods
+matching following signatures:
+
+.. code-block:: c++
+
+ size_type size() const;
+ bool empty() const;
+
+`size_type` can be any kind of integer type.
--- /dev/null
+.. title:: clang-tidy - readability-delete-null-pointer
+
+readability-delete-null-pointer
+===============================
+
+Checks the ``if`` statements where a pointer's existence is checked and then deletes the pointer.
+The check is unnecessary as deleting a null pointer has no effect.
+
+.. code:: c++
+
+ int *p;
+ if (p)
+ delete p;
--- /dev/null
+.. title:: clang-tidy - readability-deleted-default
+
+readability-deleted-default
+===========================
+
+Checks that constructors and assignment operators marked as ``= default`` are
+not actually deleted by the compiler.
+
+.. code-block:: c++
+
+ class Example {
+ public:
+ // This constructor is deleted because I is missing a default value.
+ Example() = default;
+ // This is fine.
+ Example(const Example& Other) = default;
+ // This operator is deleted because I cannot be assigned (it is const).
+ Example& operator=(const Example& Other) = default;
+
+ private:
+ const int I;
+ };
--- /dev/null
+.. title:: clang-tidy - readability-else-after-return
+
+readability-else-after-return
+=============================
+
+`LLVM Coding Standards <http://llvm.org/docs/CodingStandards.html>`_ advises to
+reduce indentation where possible and where it makes understanding code easier.
+Early exit is one of the suggested enforcements of that. Please do not use
+``else`` or ``else if`` after something that interrupts control flow - like
+``return``, ``break``, ``continue``, ``throw``.
+
+The following piece of code illustrates how the check works. This piece of code:
+
+.. code-block:: c++
+
+ void foo(int Value) {
+ int Local = 0;
+ for (int i = 0; i < 42; i++) {
+ if (Value == 1) {
+ return;
+ } else {
+ Local++;
+ }
+
+ if (Value == 2)
+ continue;
+ else
+ Local++;
+
+ if (Value == 3) {
+ throw 42;
+ } else {
+ Local++;
+ }
+ }
+ }
+
+
+Would be transformed into:
+
+.. code-block:: c++
+
+ void foo(int Value) {
+ int Local = 0;
+ for (int i = 0; i < 42; i++) {
+ if (Value == 1) {
+ return;
+ }
+ Local++;
+
+ if (Value == 2)
+ continue;
+ Local++;
+
+ if (Value == 3) {
+ throw 42;
+ }
+ Local++;
+ }
+ }
+
+
+This check helps to enforce this `LLVM Coding Standards recommendation
+<http://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return>`_.
--- /dev/null
+.. title:: clang-tidy - readability-function-size
+
+readability-function-size
+=========================
+
+`google-readability-function-size` redirects here as an alias for this check.
+
+Checks for large functions based on various metrics.
+
+Options
+-------
+
+.. option:: LineThreshold
+
+ Flag functions exceeding this number of lines. The default is `-1` (ignore
+ the number of lines).
+
+.. option:: StatementThreshold
+
+ Flag functions exceeding this number of statements. This may differ
+ significantly from the number of lines for macro-heavy code. The default is
+ `800`.
+
+.. option:: BranchThreshold
+
+ Flag functions exceeding this number of control statements. The default is
+ `-1` (ignore the number of branches).
+
+.. option:: ParameterThreshold
+
+ Flag functions that exceed a specified number of parameters. The default
+ is `-1` (ignore the number of parameters).
+
+.. option:: NestingThreshold
+
+ Flag compound statements which create next nesting level after
+ `NestingThreshold`. This may differ significantly from the expected value
+ for macro-heavy code. The default is `-1` (ignore the nesting level).
--- /dev/null
+.. title:: clang-tidy - readability-identifier-naming
+
+readability-identifier-naming
+=============================
+
+Checks for identifiers naming style mismatch.
+
+This check will try to enforce coding guidelines on the identifiers naming.
+It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing and
+tries to convert from one to another if a mismatch is detected.
+
+It also supports a fixed prefix and suffix that will be prepended or
+appended to the identifiers, regardless of the casing.
+
+Many configuration options are available, in order to be able to create
+different rules for different kind of identifier. In general, the
+rules are falling back to a more generic rule if the specific case is not
+configured.
--- /dev/null
+.. title:: clang-tidy - readability-implicit-bool-cast
+
+readability-implicit-bool-cast
+==============================
+
+This check can be used to find implicit conversions between built-in types and
+booleans. Depending on use case, it may simply help with readability of the code,
+or in some cases, point to potential bugs which remain unnoticed due to implicit
+conversions.
+
+The following is a real-world example of bug which was hiding behind implicit
+``bool`` cast:
+
+.. code-block:: c++
+
+ class Foo {
+ int m_foo;
+
+ public:
+ void setFoo(bool foo) { m_foo = foo; } // warning: implicit cast bool -> int
+ int getFoo() { return m_foo; }
+ };
+
+ void use(Foo& foo) {
+ bool value = foo.getFoo(); // warning: implicit cast int -> bool
+ }
+
+This code is the result of unsuccessful refactoring, where type of ``m_foo``
+changed from ``bool`` to ``int``. The programmer forgot to change all
+occurrences of ``bool``, and the remaining code is no longer correct, yet it
+still compiles without any visible warnings.
+
+In addition to issuing warnings, fix-it hints are provided to help solve the
+reported issues. This can be used for improving readability of code, for
+example:
+
+.. code-block:: c++
+
+ void conversionsToBool() {
+ float floating;
+ bool boolean = floating;
+ // ^ propose replacement: bool boolean = floating != 0.0f;
+
+ int integer;
+ if (integer) {}
+ // ^ propose replacement: if (integer != 0) {}
+
+ int* pointer;
+ if (!pointer) {}
+ // ^ propose replacement: if (pointer == nullptr) {}
+
+ while (1) {}
+ // ^ propose replacement: while (true) {}
+ }
+
+ void functionTakingInt(int param);
+
+ void conversionsFromBool() {
+ bool boolean;
+ functionTakingInt(boolean);
+ // ^ propose replacement: functionTakingInt(static_cast<int>(boolean));
+
+ functionTakingInt(true);
+ // ^ propose replacement: functionTakingInt(1);
+ }
+
+In general, the following cast types are checked:
+
+- integer expression/literal to boolean,
+
+- floating expression/literal to boolean,
+
+- pointer/pointer to member/``nullptr``/``NULL`` to boolean,
+
+- boolean expression/literal to integer,
+
+- boolean expression/literal to floating.
+
+The rules for generating fix-it hints are:
+
+- in case of casts from other built-in type to bool, an explicit comparison
+ is proposed to make it clear what exaclty is being compared:
+
+ - ``bool boolean = floating;`` is changed to
+ ``bool boolean = floating == 0.0f;``,
+
+ - for other types, appropriate literals are used (``0``, ``0u``, ``0.0f``,
+ ``0.0``, ``nullptr``),
+
+- in case of negated expressions cast to bool, the proposed replacement with
+ comparison is simplified:
+
+ - ``if (!pointer)`` is changed to ``if (pointer == nullptr)``,
+
+- in case of casts from bool to other built-in types, an explicit ``static_cast``
+ is proposed to make it clear that a cast is taking place:
+
+ - ``int integer = boolean;`` is changed to
+ ``int integer = static_cast<int>(boolean);``,
+
+- if the cast is performed on type literals, an equivalent literal is proposed,
+ according to what type is actually expected, for example:
+
+ - ``functionTakingBool(0);`` is changed to ``functionTakingBool(false);``,
+
+ - ``functionTakingInt(true);`` is changed to ``functionTakingInt(1);``,
+
+ - for other types, appropriate literals are used (``false``, ``true``, ``0``,
+ ``1``, ``0u``, ``1u``, ``0.0f``, ``1.0f``, ``0.0``, ``1.0f``).
+
+Some additional accommodations are made for pre-C++11 dialects:
+
+- ``false`` literal cast to pointer is detected,
+
+- instead of ``nullptr`` literal, ``0`` is proposed as replacement.
+
+Occurrences of implicit casts inside macros and template instantiations are
+deliberately ignored, as it is not clear how to deal with such cases.
+
+Options
+-------
+
+.. option:: AllowConditionalIntegerCasts
+
+ When non-zero, the check will allow conditional integer casts. Default is
+ `0`.
+
+.. option:: AllowConditionalPointerCasts
+
+ When non-zero, the check will allow conditional pointer casts. Default is `0`.
--- /dev/null
+.. title:: clang-tidy - readability-inconsistent-declaration-parameter-name
+
+readability-inconsistent-declaration-parameter-name
+===================================================
+
+Find function declarations which differ in parameter names.
+
+Example:
+
+.. code-block:: c++
+
+ // in foo.hpp:
+ void foo(int a, int b, int c);
+
+ // in foo.cpp:
+ void foo(int d, int e, int f); // warning
+
+This check should help to enforce consistency in large projects, where it often
+happens that a definition of function is refactored, changing the parameter
+names, but its declaration in header file is not updated. With this check, we
+can easily find and correct such inconsistencies, keeping declaration and
+definition always in sync.
+
+Unnamed parameters are allowed and are not taken into account when comparing
+function declarations, for example:
+
+.. code-block:: c++
+
+ void foo(int a);
+ void foo(int); // no warning
+
+To help with refactoring, in some cases fix-it hints are generated to align
+parameter names to a single naming convention. This works with the assumption
+that the function definition is the most up-to-date version, as it directly
+references parameter names in its body. Example:
+
+.. code-block:: c++
+
+ void foo(int a); // warning and fix-it hint (replace "a" to "b")
+ int foo(int b) { return b + 2; } // definition with use of "b"
+
+In the case of multiple redeclarations or function template specializations,
+a warning is issued for every redeclaration or specialization inconsistent with
+the definition or the first declaration seen in a translation unit.
--- /dev/null
+.. title:: clang-tidy - readability-misleading-indentation
+
+readability-misleading-indentation
+==================================
+
+Correct indentation helps to understand code. Mismatch of the syntactical
+structure and the indentation of the code may hide serious problems.
+Missing braces can also make it significantly harder to read the code,
+therefore it is important to use braces.
+
+The way to avoid dangling else is to always check that an ``else`` belongs
+to the ``if`` that begins in the same column.
+
+You can omit braces when your inner part of e.g. an ``if`` statement has only
+one statement in it. Although in that case you should begin the next statement
+in the same column with the ``if``.
+
+Examples:
+
+.. code-block:: c++
+
+ // Dangling else:
+ if (cond1)
+ if (cond2)
+ foo1();
+ else
+ foo2(); // Wrong indentation: else belongs to if(cond2) statement.
+
+ // Missing braces:
+ if (cond1)
+ foo1();
+ foo2(); // Not guarded by if(cond1).
+
+Limitations
+-----------
+
+Note that this check only works as expected when the tabs or spaces are used
+consistently and not mixed.
--- /dev/null
+.. title:: clang-tidy - readability-misplaced-array-index
+
+readability-misplaced-array-index
+=================================
+
+This check warns for unusual array index syntax.
+
+The following code has unusual array index syntax:
+
+.. code-block:: c++
+
+ void f(int *X, int Y) {
+ Y[X] = 0;
+ }
+
+becomes
+
+.. code-block:: c++
+
+ void f(int *X, int Y) {
+ X[Y] = 0;
+ }
+
+The check warns about such unusual syntax for readability reasons:
+ * There are programmers that are not familiar with this unusual syntax.
+ * It is possible that variables are mixed up.
+
--- /dev/null
+.. title:: clang-tidy - readability-named-parameter
+
+readability-named-parameter
+===========================
+
+Find functions with unnamed arguments.
+
+The check implements the following rule originating in the Google C++ Style
+Guide:
+
+https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
+
+All parameters should be named, with identical names in the declaration and
+implementation.
+
+Corresponding cpplint.py check name: `readability/function`.
--- /dev/null
+.. title:: clang-tidy - readability-non-const-parameter
+
+readability-non-const-parameter
+===============================
+
+The check finds function parameters of a pointer type that could be changed to
+point to a constant type instead.
+
+When ``const`` is used properly, many mistakes can be avoided. Advantages when
+using ``const`` properly:
+
+- prevent unintentional modification of data;
+
+- get additional warnings such as using uninitialized data;
+
+- make it easier for developers to see possible side effects.
+
+This check is not strict about constness, it only warns when the constness will
+make the function interface safer.
+
+.. code-block:: c++
+
+ // warning here; the declaration "const char *p" would make the function
+ // interface safer.
+ char f1(char *p) {
+ return *p;
+ }
+
+ // no warning; the declaration could be more const "const int * const p" but
+ // that does not make the function interface safer.
+ int f2(const int *p) {
+ return *p;
+ }
+
+ // no warning; making x const does not make the function interface safer
+ int f3(int x) {
+ return x;
+ }
+
+ // no warning; Technically, *p can be const ("const struct S *p"). But making
+ // *p const could be misleading. People might think that it's safe to pass
+ // const data to this function.
+ struct S { int *a; int *b; };
+ int f3(struct S *p) {
+ *(p->a) = 0;
+ }
--- /dev/null
+.. title:: clang-tidy - readability-redundant-control-flow
+
+readability-redundant-control-flow
+==================================
+
+This check looks for procedures (functions returning no value) with ``return``
+statements at the end of the function. Such ``return`` statements are redundant.
+
+Loop statements (``for``, ``while``, ``do while``) are checked for redundant
+``continue`` statements at the end of the loop body.
+
+Examples:
+
+The following function `f` contains a redundant ``return`` statement:
+
+.. code-block:: c++
+
+ extern void g();
+ void f() {
+ g();
+ return;
+ }
+
+becomes
+
+.. code-block:: c++
+
+ extern void g();
+ void f() {
+ g();
+ }
+
+The following function `k` contains a redundant ``continue`` statement:
+
+.. code-block:: c++
+
+ void k() {
+ for (int i = 0; i < 10; ++i) {
+ continue;
+ }
+ }
+
+becomes
+
+.. code-block:: c++
+
+ void k() {
+ for (int i = 0; i < 10; ++i) {
+ }
+ }
--- /dev/null
+.. title:: clang-tidy - readability-redundant-declaration
+
+readability-redundant-declaration
+=================================
+
+Finds redundant variable and function declarations.
+
+.. code-block:: c++
+
+ extern int X;
+ extern int X;
+
+becomes
+
+.. code-block:: c++
+
+ extern int X;
+
+Such redundant declarations can be removed without changing program behaviour.
+They can for instance be unintentional left overs from previous refactorings
+when code has been moved around. Having redundant declarations could in worst
+case mean that there are typos in the code that cause bugs.
+
+Normally the code can be automatically fixed, :program:`clang-tidy` can remove
+the second declaration. However there are 2 cases when you need to fix the code
+manually:
+
+* When the declarations are in different header files;
+* When multiple variables are declared together.
--- /dev/null
+.. title:: clang-tidy - readability-redundant-function-ptr-dereference
+
+readability-redundant-function-ptr-dereference
+==============================================
+
+Finds redundant dereferences of a function pointer.
+
+Before:
+
+.. code-block:: c++
+
+ int f(int,int);
+ int (*p)(int, int) = &f;
+
+ int i = (**p)(10, 50);
+
+After:
+
+.. code-block:: c++
+
+ int f(int,int);
+ int (*p)(int, int) = &f;
+
+ int i = (*p)(10, 50);
--- /dev/null
+.. title:: clang-tidy - readability-redundant-member-init
+
+readability-redundant-member-init
+=================================
+
+Finds member initializations that are unnecessary because the same default
+constructor would be called if they were not present.
+
+Example:
+
+.. code-block:: c++
+
+ // Explicitly initializing the member s is unnecessary.
+ class Foo {
+ public:
+ Foo() : s() {}
+
+ private:
+ std::string s;
+ };
--- /dev/null
+.. title:: clang-tidy - readability-redundant-smartptr-get
+
+readability-redundant-smartptr-get
+==================================
+
+`google-readability-redundant-smartptr-get` redirects here as an alias for this
+check.
+
+Find and remove redundant calls to smart pointer's ``.get()`` method.
+
+Examples:
+
+.. code-block:: c++
+
+ ptr.get()->Foo() ==> ptr->Foo()
+ *ptr.get() ==> *ptr
+ *ptr->get() ==> **ptr
+
--- /dev/null
+.. title:: clang-tidy - readability-redundant-string-cstr
+
+readability-redundant-string-cstr
+=================================
+
+
+Finds unnecessary calls to ``std::string::c_str()`` and ``std::string::data()``.
--- /dev/null
+.. title:: clang-tidy - readability-redundant-string-init
+
+readability-redundant-string-init
+=================================
+
+Finds unnecessary string initializations.
+
+Examples:
+
+.. code-block:: c++
+
+ // Initializing string with empty string literal is unnecessary.
+ std::string a = "";
+ std::string b("");
+
+ // becomes
+
+ std::string a;
+ std::string b;
--- /dev/null
+.. title:: clang-tidy - readability-simplify-boolean-expr
+
+readability-simplify-boolean-expr
+=================================
+
+Looks for boolean expressions involving boolean constants and simplifies
+them to use the appropriate boolean expression directly.
+
+Examples:
+
+=========================================== ================
+Initial expression Result
+------------------------------------------- ----------------
+``if (b == true)`` ``if (b)``
+``if (b == false)`` ``if (!b)``
+``if (b && true)`` ``if (b)``
+``if (b && false)`` ``if (false)``
+``if (b || true)`` ``if (true)``
+``if (b || false)`` ``if (b)``
+``e ? true : false`` ``e``
+``e ? false : true`` ``!e``
+``if (true) t(); else f();`` ``t();``
+``if (false) t(); else f();`` ``f();``
+``if (e) return true; else return false;`` ``return e;``
+``if (e) return false; else return true;`` ``return !e;``
+``if (e) b = true; else b = false;`` ``b = e;``
+``if (e) b = false; else b = true;`` ``b = !e;``
+``if (e) return true; return false;`` ``return e;``
+``if (e) return false; return true;`` ``return !e;``
+=========================================== ================
+
+The resulting expression ``e`` is modified as follows:
+ 1. Unnecessary parentheses around the expression are removed.
+ 2. Negated applications of ``!`` are eliminated.
+ 3. Negated applications of comparison operators are changed to use the
+ opposite condition.
+ 4. Implicit conversions of pointers, including pointers to members, to
+ ``bool`` are replaced with explicit comparisons to ``nullptr`` in C++11
+ or ``NULL`` in C++98/03.
+ 5. Implicit casts to ``bool`` are replaced with explicit casts to ``bool``.
+ 6. Object expressions with ``explicit operator bool`` conversion operators
+ are replaced with explicit casts to ``bool``.
+ 7. Implicit conversions of integral types to ``bool`` are replaced with
+ explicit comparisons to ``0``.
+
+Examples:
+ 1. The ternary assignment ``bool b = (i < 0) ? true : false;`` has redundant
+ parentheses and becomes ``bool b = i < 0;``.
+
+ 2. The conditional return ``if (!b) return false; return true;`` has an
+ implied double negation and becomes ``return b;``.
+
+ 3. The conditional return ``if (i < 0) return false; return true;`` becomes
+ ``return i >= 0;``.
+
+ The conditional return ``if (i != 0) return false; return true;`` becomes
+ ``return i == 0;``.
+
+ 4. The conditional return ``if (p) return true; return false;`` has an
+ implicit conversion of a pointer to ``bool`` and becomes
+ ``return p != nullptr;``.
+
+ The ternary assignment ``bool b = (i & 1) ? true : false;`` has an
+ implicit conversion of ``i & 1`` to ``bool`` and becomes
+ ``bool b = (i & 1) != 0;``.
+
+ 5. The conditional return ``if (i & 1) return true; else return false;`` has
+ an implicit conversion of an integer quantity ``i & 1`` to ``bool`` and
+ becomes ``return (i & 1) != 0;``
+
+ 6. Given ``struct X { explicit operator bool(); };``, and an instance ``x`` of
+ ``struct X``, the conditional return ``if (x) return true; return false;``
+ becomes ``return static_cast<bool>(x);``
+
+Options
+-------
+
+.. option:: ChainedConditionalReturn
+
+ If non-zero, conditional boolean return statements at the end of an
+ ``if/else if`` chain will be transformed. Default is `0`.
+
+.. option:: ChainedConditionalAssignment
+
+ If non-zero, conditional boolean assignments at the end of an ``if/else
+ if`` chain will be transformed. Default is `0`.
--- /dev/null
+.. title:: clang-tidy - readability-static-definition-in-anonymous-namespace
+
+readability-static-definition-in-anonymous-namespace
+====================================================
+
+Finds static function and variable definitions in anonymous namespace.
+
+In this case, ``static`` is redundant, because anonymous namespace limits the
+visibility of definitions to a single translation unit.
+
+.. code-block:: c++
+
+ namespace {
+ static int a = 1; // Warning.
+ static const b = 1; // Warning.
+ }
+
+The check will apply a fix by removing the redundant ``static`` qualifier.
--- /dev/null
+.. title:: clang-tidy - readability-uniqueptr-delete-release
+
+readability-uniqueptr-delete-release
+====================================
+
+Replace ``delete <unique_ptr>.release()`` with ``<unique_ptr> = nullptr``.
+The latter is shorter, simpler and does not require use of raw pointer APIs.
+
+.. code-block:: c++
+
+ std::unique_ptr<int> P;
+ delete P.release();
+
+ // becomes
+
+ std::unique_ptr<int> P;
+ P = nullptr;
--- /dev/null
+==========
+Clang-Tidy
+==========
+
+.. contents::
+
+See also:
+
+.. toctree::
+ :maxdepth: 1
+
+ The list of clang-tidy checks <checks/list>
+
+:program:`clang-tidy` is a clang-based C++ "linter" tool. Its purpose is to
+provide an extensible framework for diagnosing and fixing typical programming
+errors, like style violations, interface misuse, or bugs that can be deduced via
+static analysis. :program:`clang-tidy` is modular and provides a convenient
+interface for writing new checks.
+
+
+Using clang-tidy
+================
+
+:program:`clang-tidy` is a `LibTooling`_-based tool, and it's easier to 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 ``--``:
+
+.. code-block:: console
+
+ $ clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...
+
+:program:`clang-tidy` has its own checks and can also run Clang static analyzer
+checks. Each check has a name and the checks to run can be chosen using the
+``-checks=`` option, which specifies a comma-separated list of positive and
+negative (prefixed with ``-``) globs. Positive globs add subsets of checks,
+negative globs remove them. For example,
+
+.. code-block:: console
+
+ $ clang-tidy test.cpp -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*
+
+will disable all default checks (``-*``) and enable all ``clang-analyzer-*``
+checks except for ``clang-analyzer-cplusplus*`` ones.
+
+The ``-list-checks`` option lists all the enabled checks. When used without
+``-checks=``, it shows checks enabled by default. Use ``-checks=*`` to see all
+available checks or with any other value of ``-checks=`` to see which checks are
+enabled by this value.
+
+.. _checks-groups-table:
+
+There are currently the following groups of checks:
+
+====================== =========================================================
+Name prefix Description
+====================== =========================================================
+``android-`` Checks related to Android.
+``boost-`` Checks related to Boost library.
+``bugprone-`` Checks that target bugprone code constructs.
+``cert-`` Checks related to CERT Secure Coding Guidelines.
+``cppcoreguidelines-`` Checks related to C++ Core Guidelines.
+``clang-analyzer-`` Clang Static Analyzer checks.
+``google-`` Checks related to Google coding conventions.
+``hicpp-`` Checks related to High Integrity C++ Coding Standard.
+``llvm-`` Checks related to the LLVM coding conventions.
+``misc-`` Checks that we didn't have a better category for.
+``modernize-`` Checks that advocate usage of modern (currently "modern"
+ means "C++11") language constructs.
+``mpi-`` Checks related to MPI (Message Passing Interface).
+``performance-`` Checks that target performance-related issues.
+``readability-`` Checks that target readability-related issues that don't
+ relate to any particular coding style.
+====================== =========================================================
+
+Clang diagnostics are treated in a similar way as check diagnostics. Clang
+diagnostics are displayed by :program:`clang-tidy` and can be filtered out using
+``-checks=`` option. However, the ``-checks=`` option does not affect
+compilation arguments, so it can not turn on Clang warnings which are not
+already turned on in build configuration. The ``-warnings-as-errors=`` option
+upgrades any warnings emitted under the ``-checks=`` flag to errors (but it
+does not enable any checks itself).
+
+Clang diagnostics have check names starting with ``clang-diagnostic-``.
+Diagnostics which have a corresponding warning option, are named
+``clang-diagnostic-<warning-option>``, e.g. Clang warning controlled by
+``-Wliteral-conversion`` will be reported with check name
+``clang-diagnostic-literal-conversion``.
+
+The ``-fix`` flag instructs :program:`clang-tidy` to fix found errors if
+supported by corresponding checks.
+
+An overview of all the command-line options:
+
+.. code-block:: console
+
+ $ clang-tidy -help
+ USAGE: clang-tidy [options] <source0> [... <sourceN>]
+
+ 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-tidy options:
+
+ -analyze-temporary-dtors -
+ Enable temporary destructor-aware analysis in
+ clang-analyzer- checks.
+ This option overrides the value read from a
+ .clang-tidy file.
+ -checks=<string> -
+ Comma-separated list of globs with optional '-'
+ prefix. Globs are processed in order of
+ appearance in the list. Globs without '-'
+ prefix add checks with matching names to the
+ set, globs with the '-' prefix remove checks
+ with matching names from the set of enabled
+ checks. This option's value is appended to the
+ value of the 'Checks' option in .clang-tidy
+ file, if any.
+ -config=<string> -
+ Specifies a configuration in YAML/JSON format:
+ -config="{Checks: '*',
+ CheckOptions: [{key: x,
+ value: y}]}"
+ When the value is empty, clang-tidy will
+ attempt to find a file named .clang-tidy for
+ each source file in its parent directories.
+ -dump-config -
+ Dumps configuration in the YAML format to
+ stdout. This option can be used along with a
+ file name (and '--' if the file is outside of a
+ project with configured compilation database).
+ The configuration used for this file will be
+ printed.
+ Use along with -checks=* to include
+ configuration of all checks.
+ -enable-check-profile -
+ Enable per-check timing profiles, and print a
+ report to stderr.
+ -explain-config -
+ For each enabled check explains, where it is
+ enabled, i.e. in clang-tidy binary, command
+ line or a specific configuration file.
+ -export-fixes=<filename> -
+ YAML file to store suggested fixes in. The
+ stored fixes can be applied to the input source
+ code with clang-apply-replacements.
+ -extra-arg=<string> - Additional argument to append to the compiler command line
+ -extra-arg-before=<string> - Additional argument to prepend to the compiler command line
+ -fix -
+ Apply suggested fixes. Without -fix-errors
+ clang-tidy will bail out if any compilation
+ errors were found.
+ -fix-errors -
+ Apply suggested fixes even if compilation
+ errors were found. If compiler errors have
+ attached fix-its, clang-tidy will apply them as
+ well.
+ -format-style=<string> -
+ Style for formatting code around applied fixes:
+ - 'none' (default) turns off formatting
+ - 'file' (literally 'file', not a placeholder)
+ uses .clang-format file in the closest parent
+ directory
+ - '{ <json> }' specifies options inline, e.g.
+ -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
+ - 'llvm', 'google', 'webkit', 'mozilla'
+ See clang-format documentation for the up-to-date
+ information about formatting styles and options.
+ This option overrides the 'FormatStyle` option in
+ .clang-tidy file, if any.
+ -header-filter=<string> -
+ Regular expression matching the names of the
+ headers to output diagnostics from. Diagnostics
+ from the main file of each translation unit are
+ always displayed.
+ Can be used together with -line-filter.
+ This option overrides the 'HeaderFilter' option
+ in .clang-tidy file, if any.
+ -line-filter=<string> -
+ List of files with line ranges to filter the
+ warnings. Can be used together with
+ -header-filter. The format of the list is a
+ JSON array of objects:
+ [
+ {"name":"file1.cpp","lines":[[1,3],[5,7]]},
+ {"name":"file2.h"}
+ ]
+ -list-checks -
+ List all enabled checks and exit. Use with
+ -checks=* to list all available checks.
+ -p=<string> - Build path
+ -quiet -
+ Run clang-tidy in quiet mode. This suppresses
+ printing statistics about ignored warnings and
+ warnings treated as errors if the respective
+ options are specified.
+ -system-headers - Display the errors from system headers.
+ -warnings-as-errors=<string> -
+ Upgrades warnings to errors. Same format as
+ '-checks'.
+ This option's value is appended to the value of
+ the 'WarningsAsErrors' option in .clang-tidy
+ file, if any.
+
+ -p <build-path> is used to read a compile command database.
+
+ For example, it can be a CMake build directory in which a file named
+ compile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+ CMake option to get this output). When no build path is specified,
+ a search for compile_commands.json will be attempted through all
+ parent paths of the first input file . See:
+ http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an
+ example of setting up Clang Tooling on a source tree.
+
+ <source0> ... specify the paths of source files. These paths are
+ looked up in the compile command database. If the path of a file is
+ absolute, it needs to point into CMake's source tree. If the path is
+ relative, the current working directory needs to be in the CMake
+ source tree and the file must be in a subdirectory of the current
+ working directory. "./" prefixes in the relative files will be
+ automatically removed, but the rest of a relative path must be a
+ suffix of a path in the compile command database.
+
+
+ Configuration files:
+ clang-tidy attempts to read configuration for each source file from a
+ .clang-tidy file located in the closest parent directory of the source
+ file. If any configuration options have a corresponding command-line
+ option, command-line option takes precedence. The effective
+ configuration can be inspected using -dump-config:
+
+ $ clang-tidy -dump-config
+ ---
+ Checks: '-*,some-check'
+ WarningsAsErrors: ''
+ HeaderFilterRegex: ''
+ AnalyzeTemporaryDtors: false
+ FormatStyle: none
+ User: user
+ CheckOptions:
+ - key: some-check.SomeOption
+ value: 'some value'
+ ...
+
+.. _LibTooling: http://clang.llvm.org/docs/LibTooling.html
+.. _How To Setup Tooling For LLVM: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+
+
+Getting Involved
+================
+
+:program:`clang-tidy` has several own checks and can run Clang static analyzer
+checks, but its power is in the ability to easily write custom checks.
+
+Checks are organized in modules, which can be linked into :program:`clang-tidy`
+with minimal or no code changes in :program:`clang-tidy`.
+
+Checks can plug into the analysis on the preprocessor level using `PPCallbacks`_
+or on the AST level using `AST Matchers`_. When an error is found, checks can
+report them in a way similar to how Clang diagnostics work. A fix-it hint can be
+attached to a diagnostic message.
+
+The interface provided by :program:`clang-tidy` makes it easy to write useful
+and precise checks in just a few lines of code. If you have an idea for a good
+check, the rest of this document explains how to do this.
+
+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 suggests, renames an existing
+ check;
+ * :program:`clang-query` is invaluable for interactive prototyping of AST
+ matchers and exploration of the Clang AST;
+ * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``)
+ provides a convenient way to dump AST of a C++ program.
+
+
+.. _AST Matchers: http://clang.llvm.org/docs/LibASTMatchers.html
+.. _PPCallbacks: http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html
+.. _clang-check: http://clang.llvm.org/docs/ClangCheck.html
+
+
+Choosing the Right Place for your Check
+---------------------------------------
+
+If you have an idea of a check, you should decide whether it should be
+implemented as a:
+
++ *Clang diagnostic*: if the check is generic enough, targets code patterns that
+ most probably are bugs (rather than style or readability issues), can be
+ implemented effectively and with extremely low false positive rate, it may
+ make a good Clang diagnostic.
+
++ *Clang static analyzer check*: if the check requires some sort of control flow
+ analysis, it should probably be implemented as a static analyzer check.
+
++ *clang-tidy check* is a good choice for linter-style checks, checks that are
+ related to a certain coding style, checks that address code readability, etc.
+
+
+Preparing your Workspace
+------------------------
+
+If you are new to LLVM development, you should read the `Getting Started with
+the LLVM System`_, `Using Clang Tools`_ and `How To Setup Tooling For LLVM`_
+documents to check out and build LLVM, Clang and Clang Extra Tools with CMake.
+
+Once you are done, change to the ``llvm/tools/clang/tools/extra`` directory, and
+let's start!
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Using Clang Tools: http://clang.llvm.org/docs/ClangTools.html
+
+
+The Directory Structure
+-----------------------
+
+:program:`clang-tidy` source code resides in the
+``llvm/tools/clang/tools/extra`` directory and is structured as follows:
+
+::
+
+ clang-tidy/ # Clang-tidy core.
+ |-- ClangTidy.h # Interfaces for users and checks.
+ |-- ClangTidyModule.h # Interface for clang-tidy modules.
+ |-- ClangTidyModuleRegistry.h # Interface for registering of modules.
+ ...
+ |-- google/ # Google clang-tidy module.
+ |-+
+ |-- GoogleTidyModule.cpp
+ |-- GoogleTidyModule.h
+ ...
+ |-- llvm/ # LLVM clang-tidy module.
+ |-+
+ |-- LLVMTidyModule.cpp
+ |-- LLVMTidyModule.h
+ ...
+ |-- tool/ # Sources of the clang-tidy binary.
+ ...
+ test/clang-tidy/ # Integration tests.
+ ...
+ unittests/clang-tidy/ # Unit tests.
+ |-- ClangTidyTest.h
+ |-- GoogleModuleTest.cpp
+ |-- LLVMModuleTest.cpp
+ ...
+
+
+Writing a clang-tidy Check
+--------------------------
+
+So you have an idea of a useful check for :program:`clang-tidy`.
+
+First, if you're not familiar with LLVM development, read through the `Getting
+Started with LLVM`_ document for instructions on setting up your workflow and
+the `LLVM Coding Standards`_ document to familiarize yourself with the coding
+style used in the project. For code reviews we mostly use `LLVM Phabricator`_.
+
+.. _Getting Started with LLVM: http://llvm.org/docs/GettingStarted.html
+.. _LLVM Coding Standards: http://llvm.org/docs/CodingStandards.html
+.. _LLVM Phabricator: http://llvm.org/docs/Phabricator.html
+
+Next, you need to decide which module the check belongs to. Modules
+are located in subdirectories of `clang-tidy/
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/>`_
+and contain checks targeting a certain aspect of code quality (performance,
+readability, etc.), certain coding style or standard (Google, LLVM, CERT, etc.)
+or a widely used API (e.g. MPI). Their names are same as user-facing check
+groups names described :ref:`above <checks-groups-table>`.
+
+After choosing the module and the name for the check, run the
+``clang-tidy/add_new_check.py`` script to create the skeleton of the check and
+plug it to :program:`clang-tidy`. It's the recommended way of adding new checks.
+
+If we want to create a `readability-awesome-function-names`, we would run:
+
+.. code-block:: console
+
+ $ clang-tidy/add_new_check.py readability awesome-function-names
+
+
+The ``add_new_check.py`` script will:
+ * create the class for your check inside the specified module's directory and
+ register it in the module and in the build system;
+ * create a lit test file in the ``test/clang-tidy/`` directory;
+ * create a documentation file and include it into the
+ ``docs/clang-tidy/checks/list.rst``.
+
+Let's see in more detail at the check class definition:
+
+.. code-block:: c++
+
+ ...
+
+ #include "../ClangTidy.h"
+
+ namespace clang {
+ namespace tidy {
+ namespace readability {
+
+ ...
+ class AwesomeFunctionNamesCheck : public ClangTidyCheck {
+ public:
+ AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ };
+
+ } // namespace readability
+ } // namespace tidy
+ } // namespace clang
+
+ ...
+
+Constructor of the check receives the ``Name`` and ``Context`` parameters, and
+must forward them to the ``ClangTidyCheck`` constructor.
+
+In our case the check needs to operate on the AST level and it overrides the
+``registerMatchers`` and ``check`` methods. If we wanted to analyze code on the
+preprocessor level, we'd need instead to override the ``registerPPCallbacks``
+method.
+
+In the ``registerMatchers`` method we create an AST Matcher (see `AST Matchers`_
+for more information) that will find the pattern in the AST that we want to
+inspect. The results of the matching are passed to the ``check`` method, which
+can further inspect them and report diagnostics.
+
+.. code-block:: c++
+
+ using namespace ast_matchers;
+
+ void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl().bind("x"), this);
+ }
+
+ void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
+ if (MatchedDecl->getName().startswith("awesome_"))
+ return;
+ diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome")
+ << MatchedDecl
+ << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
+ }
+
+(If you want to see an example of a useful check, look at
+`clang-tidy/google/ExplicitConstructorCheck.h
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.h>`_
+and `clang-tidy/google/ExplicitConstructorCheck.cpp
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.cpp>`_).
+
+
+Registering your Check
+----------------------
+
+(The ``add_new_check.py`` takes care of registering the check in an existing
+module. If you want to create a new module or know the details, read on.)
+
+The check should be registered in the corresponding module with a distinct name:
+
+.. code-block:: c++
+
+ class MyModule : public ClangTidyModule {
+ public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<ExplicitConstructorCheck>(
+ "my-explicit-constructor");
+ }
+ };
+
+Now we need to register the module in the ``ClangTidyModuleRegistry`` using a
+statically initialized variable:
+
+.. code-block:: c++
+
+ static ClangTidyModuleRegistry::Add<MyModule> X("my-module",
+ "Adds my lint checks.");
+
+
+When using LLVM build system, we need to use the following hack to ensure the
+module is linked into the :program:`clang-tidy` binary:
+
+Add this near the ``ClangTidyModuleRegistry::Add<MyModule>`` variable:
+
+.. code-block:: c++
+
+ // This anchor is used to force the linker to link in the generated object file
+ // and thus register the MyModule.
+ volatile int MyModuleAnchorSource = 0;
+
+And this to the main translation unit of the :program:`clang-tidy` binary (or
+the binary you link the ``clang-tidy`` library in)
+``clang-tidy/tool/ClangTidyMain.cpp``:
+
+.. code-block:: c++
+
+ // This anchor is used to force the linker to link the MyModule.
+ extern volatile int MyModuleAnchorSource;
+ static int MyModuleAnchorDestination = MyModuleAnchorSource;
+
+
+Configuring Checks
+------------------
+
+If a check needs configuration options, it can access check-specific options
+using the ``Options.get<Type>("SomeOption", DefaultValue)`` call in the check
+constructor. In this case the check should also override the
+``ClangTidyCheck::storeOptions`` method to make the options provided by the
+check discoverable. This method lets :program:`clang-tidy` know which options
+the check implements and what the current values are (e.g. for the
+``-dump-config`` command line option).
+
+.. code-block:: c++
+
+ class MyCheck : public ClangTidyCheck {
+ const unsigned SomeOption1;
+ const std::string SomeOption2;
+
+ public:
+ MyCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ SomeOption(Options.get("SomeOption1", -1U)),
+ SomeOption(Options.get("SomeOption2", "some default")) {}
+
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+ Options.store(Opts, "SomeOption1", SomeOption1);
+ Options.store(Opts, "SomeOption2", SomeOption2);
+ }
+ ...
+
+Assuming the check is registered with the name "my-check", the option can then
+be set in a ``.clang-tidy`` file in the following way:
+
+.. code-block:: yaml
+
+ CheckOptions:
+ - key: my-check.SomeOption1
+ value: 123
+ - key: my-check.SomeOption2
+ value: 'some other value'
+
+If you need to specify check options on a command line, you can use the inline
+YAML format:
+
+.. code-block:: console
+
+ $ clang-tidy -config="{CheckOptions: [{key: a, value: b}, {key: x, value: y}]}" ...
+
+
+Testing Checks
+--------------
+
+To run tests for :program:`clang-tidy` use the command:
+
+.. code-block:: console
+
+ $ ninja check-clang-tools
+
+:program:`clang-tidy` checks can be tested using either unit tests or
+`lit`_ tests. Unit tests may be more convenient to test complex replacements
+with strict checks. `Lit`_ tests allow using partial text matching and regular
+expressions which makes them more suitable for writing compact tests for
+diagnostic messages.
+
+The ``check_clang_tidy.py`` script provides an easy way to test both
+diagnostic messages and fix-its. It filters out ``CHECK`` lines from the test
+file, runs :program:`clang-tidy` and verifies messages and fixes with two
+separate `FileCheck`_ invocations: once with FileCheck's directive
+prefix set to ``CHECK-MESSAGES``, validating the diagnostic messages,
+and once with the directive prefix set to ``CHECK-FIXES``, running
+against the fixed code (i.e., the code after generated fix-its are
+applied). In particular, ``CHECK-FIXES:`` can be used to check
+that code was not modified by fix-its, by checking that it is present
+unchanged in the fixed code. The full set of `FileCheck`_ directives
+is available (e.g., ``CHECK-MESSAGES-SAME:``, ``CHECK-MESSAGES-NOT:``), though
+typically the basic ``CHECK`` forms (``CHECK-MESSAGES`` and ``CHECK-FIXES``)
+are sufficient for clang-tidy tests. Note that the `FileCheck`_
+documentation mostly assumes the default prefix (``CHECK``), and hence
+describes the directive as ``CHECK:``, ``CHECK-SAME:``, ``CHECK-NOT:``, etc.
+Replace ``CHECK`` by either ``CHECK-FIXES`` or ``CHECK-MESSAGES`` for
+clang-tidy tests.
+
+An additional check enabled by ``check_clang_tidy.py`` ensures that
+if `CHECK-MESSAGES:` is used in a file then every warning or error
+must have an associated CHECK in that file.
+
+To use the ``check_clang_tidy.py`` script, put a .cpp file with the
+appropriate ``RUN`` line in the ``test/clang-tidy`` directory. Use
+``CHECK-MESSAGES:`` and ``CHECK-FIXES:`` lines to write checks against
+diagnostic messages and fixed code.
+
+It's advised to make the checks as specific as possible to avoid checks matching
+to incorrect parts of the input. Use ``[[@LINE+X]]``/``[[@LINE-X]]``
+substitutions and distinct function and variable names in the test code.
+
+Here's an example of a test using the ``check_clang_tidy.py`` script (the full
+source code is at `test/clang-tidy/google-readability-casting.cpp`_):
+
+.. code-block:: c++
+
+ // RUN: %check_clang_tidy %s google-readability-casting %t
+
+ void f(int a) {
+ int b = (int)a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting]
+ // CHECK-FIXES: int b = a;
+ }
+
+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 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
+ depending on the macro expansion/template instantiation;
+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 multiple times; :program:`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
+ macro expansions/template instantiations, but easily break some other
+ expansions/instantiations.
+
+.. _lit: http://llvm.org/docs/CommandGuide/lit.html
+.. _FileCheck: http://llvm.org/docs/CommandGuide/FileCheck.html
+.. _test/clang-tidy/google-readability-casting.cpp: http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp
+
+
+Running clang-tidy on LLVM
+--------------------------
+
+To test a check it's best to try it out on a larger code base. LLVM and Clang
+are the natural targets as you already have the source code around. The most
+convenient way to run :program:`clang-tidy` is with a compile command database;
+CMake can automatically generate one, for a description of how to enable it see
+`How To Setup Tooling For LLVM`_. Once ``compile_commands.json`` is in place and
+a working version of :program:`clang-tidy` is in ``PATH`` the entire code base
+can be analyzed with ``clang-tidy/tool/run-clang-tidy.py``. The script executes
+:program:`clang-tidy` with the default set of checks on every translation unit
+in the compile command database and displays the resulting warnings and errors.
+The script provides multiple configuration flags.
+
+* The default set of checks can be overridden using the ``-checks`` argument,
+ taking the identical format as :program:`clang-tidy` does. For example
+ ``-checks=-*,modernize-use-override`` will run the ``modernize-use-override``
+ check only.
+
+* To restrict the files examined you can provide one or more regex arguments
+ that the file names are matched against.
+ ``run-clang-tidy.py clang-tidy/.*Check\.cpp`` will only analyze clang-tidy
+ checks. It may also be necessary to restrict the header files warnings are
+ displayed from using the ``-header-filter`` flag. It has the same behavior
+ as the corresponding :program:`clang-tidy` flag.
+
+* To apply suggested fixes ``-fix`` can be passed as an argument. This gathers
+ all changes in a temporary directory and applies them. Passing ``-format``
+ will run clang-format over changed lines.
+
--- /dev/null
+============
+Clangd
+============
+
+.. contents::
+
+.. toctree::
+ :maxdepth: 1
+
+:program:`Clangd` is an implementation of the `Language Server Protocol
+<https://github.com/Microsoft/language-server-protocol>`_ leveraging Clang.
+Clangd's goal is to provide language "smartness" features like code completion,
+find references, etc. for clients such as C/C++ Editors.
+
+Using Clangd
+==================
+
+:program:`Clangd` is not meant to be used by C/C++ developers directly but
+rather from a client implementing the protocol. A client would be typically
+implemented in an IDE or an editor.
+
+At the moment, `Visual Studio Code <https://code.visualstudio.com/>`_ is mainly
+used in order to test :program:`Clangd` but more clients are likely to make
+use of :program:`Clangd` in the future as it matures and becomes a production
+quality tool. If you are interested in trying :program:`Clangd` in combination
+with Visual Studio Code, you can start by `building Clangd`_, then open Visual
+Studio Code in the clangd-vscode folder and launch the extension.
+
+Building Clangd
+==================
+
+You can follow the instructions for `building Clang
+<https://clang.llvm.org/get_started.html>`_ but "extra Clang tools" is **not**
+optional.
+
+Current Status
+==================
+
+Many features could be implemented in :program:`Clangd`.
+Here is a list of features that could be useful with the status of whether or
+not they are already implemented in :program:`Clangd` and specified in the
+Language Server Protocol. Note that for some of the features, it is not clear
+whether or not they should be part of the Language Server Protocol, so those
+features might be eventually developed outside :program:`Clangd`.
+
++-------------------------------------+------------+----------+
+| C/C++ Editor feature | LSP | Clangd |
++=====================================+============+==========+
+| Formatting | Yes | Yes |
++-------------------------------------+------------+----------+
+| Completion | Yes | Yes |
++-------------------------------------+------------+----------+
+| Diagnostics | Yes | Yes |
++-------------------------------------+------------+----------+
+| Fix-its | Yes | Yes |
++-------------------------------------+------------+----------+
+| Go to Definition | Yes | Yes |
++-------------------------------------+------------+----------+
+| Source hover | Yes | No |
++-------------------------------------+------------+----------+
+| Signature Help | Yes | No |
++-------------------------------------+------------+----------+
+| Find References | Yes | No |
++-------------------------------------+------------+----------+
+| Document Highlights | Yes | No |
++-------------------------------------+------------+----------+
+| Rename | Yes | No |
++-------------------------------------+------------+----------+
+| Code Lens | Yes | No |
++-------------------------------------+------------+----------+
+| Syntax and Semantic Coloring | No | No |
++-------------------------------------+------------+----------+
+| Code folding | No | No |
++-------------------------------------+------------+----------+
+| Call hierarchy | No | No |
++-------------------------------------+------------+----------+
+| Type hierarchy | No | No |
++-------------------------------------+------------+----------+
+| Organize Includes | No | No |
++-------------------------------------+------------+----------+
+| Quick Assist | No | No |
++-------------------------------------+------------+----------+
+| Extract Local Variable | No | No |
++-------------------------------------+------------+----------+
+| Extract Function/Method | No | No |
++-------------------------------------+------------+----------+
+| Hide Method | No | No |
++-------------------------------------+------------+----------+
+| Implement Method | No | No |
++-------------------------------------+------------+----------+
+| Gen. Getters/Setters | No | No |
++-------------------------------------+------------+----------+
+
+Getting Involved
+==================
+
+A good place for interested contributors is the `Clang developer mailing list
+<http://lists.llvm.org/mailman/listinfo/cfe-dev>`_.
+If you're also interested in contributing patches to :program:`Clangd`, take a
+look at the `LLVM Developer Policy
+<http://llvm.org/docs/DeveloperPolicy.html>`_ and `Code Reviews
+<http://llvm.org/docs/Phabricator.html>`_ page. Contributions of new features
+to the `Language Server Protocol
+<https://github.com/Microsoft/language-server-protocol>`_ itself would also be
+very useful, so that :program:`Clangd` can eventually implement them in a
+conforming way.
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Extra Clang Tools documentation build configuration file, created by
+# sphinx-quickstart on Wed Feb 13 10:00:18 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+from datetime import date
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Extra Clang Tools'
+copyright = u'2007-%d, The Clang Team' % date.today().year
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short version.
+version = '5'
+# The full version, including alpha/beta/rc tags.
+release = '5'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'friendly'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'haiku'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ExtraClangToolsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'ExtraClangTools.tex', u'Extra Clang Tools Documentation',
+ u'The Clang Team', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'extraclangtools', u'Extra Clang Tools Documentation',
+ [u'The Clang Team'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'ExtraClangTools', u'Extra Clang Tools Documentation',
+ u'The Clang Team', 'ExtraClangTools', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
--- /dev/null
+:orphan:
+
+All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
+(see the ``modernize`` module).
--- /dev/null
+/// \mainpage clang-tools
+///
+/// \section main_intro Introduction
+/// Welcome to clang tools.
+///
+/// This documentation describes the **internal** software that makes
+/// up clang tools, not the **external** use of clang tools. For
+/// usage instructions, please see the programmer's guide or reference
+/// manual.
--- /dev/null
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = clang-tools
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @abs_builddir@/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH = ../..
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 2
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 2
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = NO
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = \
+ @abs_srcdir@/../clang-tidy \
+ @abs_srcdir@/../clang-apply-replacements \
+ @abs_srcdir@/../clang-query \
+ @abs_srcdir@/../clang-rename \
+ @abs_srcdir@/../modularize \
+ @abs_srcdir@/../pp-trace \
+ @abs_srcdir@/../tool-template \
+ @abs_srcdir@/doxygen-mainpage.dox
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = YES
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 4
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX = clang::
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = @clang_tools_doxygen_generate_qhp@
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE = @clang_tools_doxygen_qch_filename@
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = @clang_tools_doxygen_qhp_namespace@
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME = @clang_tools_doxygen_qhp_cust_filter_name@
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS = @clang_tools_doxygen_qhp_cust_filter_attrs@
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION = @clang_tools_doxygen_qhelpgenerator_path@
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = @enable_searchengine@
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = @enable_server_based_search@
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = @enable_external_search@
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL = @searchengine_url@
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID = clang-tools
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS = @extra_search_mappings@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH = @abs_srcdir@/../../../include \
+ @abs_srcdir@/../../../../../include
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = LLVM_ALIGNAS(x)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = YES
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = YES
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = @DOT_IMAGE_FORMAT@
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH = @DOT@
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
--- /dev/null
+===================
+Clang-Include-Fixer
+===================
+
+.. contents::
+
+One of the major nuisances of C++ compared to other languages is the manual
+management of ``#include`` directives in any file.
+:program:`clang-include-fixer` addresses one aspect of this problem by providing
+an automated way of adding ``#include`` directives for missing symbols in one
+translation unit.
+
+While inserting missing ``#include``, :program:`clang-include-fixer` adds
+missing namespace qualifiers to all instances of an unidentified symbol if
+the symbol is missing some prefix namespace qualifiers.
+
+Setup
+=====
+
+To use :program:`clang-include-fixer` two databases are required. Both can be
+generated with existing tools.
+
+- Compilation database. Contains the compiler commands for any given file in a
+ project and can be generated by CMake, see `How To Setup Tooling For LLVM`_.
+- Symbol index. Contains all symbol information in a project to match a given
+ identifier to a header file.
+
+Ideally both databases (``compile_commands.json`` and
+``find_all_symbols_db.yaml``) are linked into the root of the source tree they
+correspond to. Then the :program:`clang-include-fixer` can automatically pick
+them up if called with a source file from that tree. Note that by default
+``compile_commands.json`` as generated by CMake does not include header files,
+so only implementation files can be handled by tools.
+
+.. _How To Setup Tooling For LLVM: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+
+Creating a Symbol Index From a Compilation Database
+---------------------------------------------------
+
+The include fixer contains :program:`find-all-symbols`, a tool to create a
+symbol database in YAML format from a compilation database by parsing all
+source files listed in it. The following list of commands shows how to set up a
+database for LLVM, any project built by CMake should follow similar steps.
+
+.. code-block:: console
+
+ $ cd path/to/llvm-build
+ $ ninja find-all-symbols // build find-all-symbols tool.
+ $ ninja clang-include-fixer // build clang-include-fixer tool.
+ $ ls compile_commands.json # Make sure compile_commands.json exists.
+ compile_commands.json
+ $ path/to/llvm/source/tools/clang/tools/extra/include-fixer/find-all-symbols/tool/run-find-all-symbols.py
+ ... wait as clang indexes the code base ...
+ $ ln -s $PWD/find_all_symbols_db.yaml path/to/llvm/source/ # Link database into the source tree.
+ $ ln -s $PWD/compile_commands.json path/to/llvm/source/ # Also link compilation database if it's not there already.
+ $ cd path/to/llvm/source
+ $ /path/to/clang-include-fixer -db=yaml path/to/file/with/missing/include.cpp
+ Added #include "foo.h"
+
+Integrate with Vim
+------------------
+To run `clang-include-fixer` on a potentially unsaved buffer in Vim. Add the
+following key binding to your ``.vimrc``:
+
+.. code-block:: console
+
+ noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py<cr>
+
+This enables `clang-include-fixer` for NORMAL and VISUAL mode. Change
+`<leader>cf` to another binding if you need clang-include-fixer on a different
+key. The `<leader> key
+<http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader>`_
+is a reference to a specific key defined by the mapleader variable and is bound
+to backslash by default.
+
+Make sure vim can find :program:`clang-include-fixer`:
+
+- Add the path to :program:`clang-include-fixer` to the PATH environment variable.
+- Or set ``g:clang_include_fixer_path`` in vimrc: ``let g:clang_include_fixer_path=path/to/clang-include-fixer``
+
+You can customize the number of headers being shown by setting
+``let g:clang_include_fixer_maximum_suggested_headers=5``
+
+Customized settings in `.vimrc`:
+
+- ``let g:clang_include_fixer_path = "clang-include-fixer"``
+
+ Set clang-include-fixer binary file path.
+
+- ``let g:clang_include_fixer_maximum_suggested_headers = 3``
+
+ Set the maximum number of ``#includes`` to show. Default is 3.
+
+- ``let g:clang_include_fixer_increment_num = 5``
+
+ Set the increment number of #includes to show every time when pressing ``m``.
+ Default is 5.
+
+- ``let g:clang_include_fixer_jump_to_include = 0``
+
+ Set to 1 if you want to jump to the new inserted ``#include`` line. Default is
+ 0.
+
+- ``let g:clang_include_fixer_query_mode = 0``
+
+ Set to 1 if you want to insert ``#include`` for the symbol under the cursor.
+ Default is 0. Compared to normal mode, this mode won't parse the source file
+ and only search the sysmbol from database, which is faster than normal mode.
+
+See ``clang-include-fixer.py`` for more details.
+
+Integrate with Emacs
+--------------------
+To run `clang-include-fixer` on a potentially unsaved buffer in Emacs.
+Ensure that Emacs finds ``clang-include-fixer.el`` by adding the directory
+containing the file to the ``load-path`` and requiring the `clang-include-fixer`
+in your ``.emacs``:
+
+.. code-block:: console
+
+ (add-to-list 'load-path "path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/"
+ (require 'clang-include-fixer)
+
+Within Emacs the tool can be invoked with the command
+``M-x clang-include-fixer``. This will insert the header that defines the
+first undefined symbol; if there is more than one header that would define the
+symbol, the user is prompted to select one.
+
+To include the header that defines the symbol at point, run
+``M-x clang-include-fixer-at-point``.
+
+Make sure Emacs can find :program:`clang-include-fixer`:
+
+- Either add the parent directory of :program:`clang-include-fixer` to the PATH
+ environment variable, or customize the Emacs user option
+ ``clang-include-fixer-executable`` to point to the file name of the program.
+
+How it Works
+============
+
+To get the most information out of Clang at parse time,
+:program:`clang-include-fixer` runs in tandem with the parse and receives
+callbacks from Clang's semantic analysis. In particular it reuses the existing
+support for typo corrections. Whenever Clang tries to correct a potential typo
+it emits a callback to the include fixer which then looks for a corresponding
+file. At this point rich lookup information is still available, which is not
+available in the AST at a later stage.
+
+The identifier that should be typo corrected is then sent to the database, if a
+header file is returned it is added as an include directive at the top of the
+file.
+
+Currently :program:`clang-include-fixer` only inserts a single include at a
+time to avoid getting caught in follow-up errors. If multiple `#include`
+additions are desired the program can be rerun until a fix-point is reached.
--- /dev/null
+.. Extra Clang Tools documentation master file, created by
+ sphinx-quickstart on Wed Feb 13 10:00:18 2013.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+.. title:: Welcome to Extra Clang Tools's documentation!
+
+Introduction
+============
+Welcome to the clang-tools-extra project which contains extra tools built using
+Clang's tooling API's.
+
+.. toctree::
+ :maxdepth: 1
+
+ ReleaseNotes
+
+Contents
+========
+.. toctree::
+ :maxdepth: 2
+
+ clang-tidy/index
+ include-fixer
+ modularize
+ pp-trace
+ clang-rename
+ clangd
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
--- /dev/null
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\ExtraClangTools.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\ExtraClangTools.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
--- /dev/null
+.. index:: modularize
+
+==================================
+Modularize User's Manual
+==================================
+
+.. toctree::
+ :hidden:
+
+ ModularizeUsage
+
+:program:`modularize` is a standalone tool that checks whether a set of headers
+provides the consistent definitions required to use modules. For example, it
+detects whether the same entity (say, a NULL macro or size_t typedef) is
+defined in multiple headers or whether a header produces different definitions
+under different circumstances. These conditions cause modules built from the
+headers to behave poorly, and should be fixed before introducing a module
+map.
+
+:program:`modularize` also has an assistant mode option for generating
+a module map file based on the provided header list. The generated file
+is a functional module map that can be used as a starting point for a
+module.map file.
+
+Getting Started
+===============
+
+To build from source:
+
+1. Read `Getting Started with the LLVM System`_ and `Clang Tools
+ Documentation`_ for information on getting sources for LLVM, Clang, and
+ Clang Extra Tools.
+
+2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
+ directions for how to build. With sources all checked out into the
+ right place the LLVM build will build Clang Extra Tools and their
+ dependencies automatically.
+
+ * If using CMake, you can also use the ``modularize`` target to build
+ just the modularize tool and its dependencies.
+
+Before continuing, take a look at :doc:`ModularizeUsage` to see how to invoke
+modularize.
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Building LLVM with CMake: http://llvm.org/docs/CMake.html
+.. _Clang Tools Documentation: http://clang.llvm.org/docs/ClangTools.html
+
+What Modularize Checks
+======================
+
+Modularize will check for the following:
+
+* Duplicate global type and variable definitions
+* Duplicate macro definitions
+* Macro instances, 'defined(macro)', or #if, #elif, #ifdef, #ifndef conditions
+ that evaluate differently in a header
+* #include directives inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks
+* Module map header coverage completeness (in the case of a module map input
+ only)
+
+Modularize will do normal C/C++ parsing, reporting normal errors and warnings,
+but will also report special error messages like the following::
+
+ error: '(symbol)' defined at multiple locations:
+ (file):(row):(column)
+ (file):(row):(column)
+
+ error: header '(file)' has different contents depending on how it was included
+
+The latter might be followed by messages like the following::
+
+ note: '(symbol)' in (file) at (row):(column) not always provided
+
+Checks will also be performed for macro expansions, defined(macro)
+expressions, and preprocessor conditional directives that evaluate
+inconsistently, and can produce error messages like the following::
+
+ (...)/SubHeader.h:11:5:
+ #if SYMBOL == 1
+ ^
+ error: Macro instance 'SYMBOL' has different values in this header,
+ depending on how it was included.
+ 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+ (...)/Header1.h
+ (...)/SubHeader.h
+ (...)/SubHeader.h:3:9:
+ #define SYMBOL 1
+ ^
+ Macro defined here.
+ 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+ (...)/Header2.h
+ (...)/SubHeader.h
+ (...)/SubHeader.h:7:9:
+ #define SYMBOL 2
+ ^
+ Macro defined here.
+
+Checks will also be performed for '#include' directives that are
+nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
+and can produce error message like the following::
+
+ IncludeInExtern.h:2:3:
+ #include "Empty.h"
+ ^
+ error: Include directive within extern "C" {}.
+ IncludeInExtern.h:1:1:
+ extern "C" {
+ ^
+ The "extern "C" {}" block is here.
+
+.. _module-map-coverage:
+
+Module Map Coverage Check
+=========================
+
+The coverage check uses the Clang library to read and parse the
+module map file. Starting at the module map file directory, or just the
+include paths, if specified, it will collect the names of all the files it
+considers headers (no extension, .h, or .inc--if you need more, modify the
+isHeader function). It then compares the headers against those referenced
+in the module map, either explicitly named, or implicitly named via an
+umbrella directory or umbrella file, as parsed by the ModuleMap object.
+If headers are found which are not referenced or covered by an umbrella
+directory or file, warning messages will be produced, and this program
+will return an error code of 1. If no problems are found, an error code of
+0 is returned.
+
+Note that in the case of umbrella headers, this tool invokes the compiler
+to preprocess the file, and uses a callback to collect the header files
+included by the umbrella header or any of its nested includes. If any
+front end options are needed for these compiler invocations, these
+can be included on the command line after the module map file argument.
+
+Warning message have the form:
+
+ warning: module.modulemap does not account for file: Level3A.h
+
+Note that for the case of the module map referencing a file that does
+not exist, the module map parser in Clang will (at the time of this
+writing) display an error message.
+
+To limit the checks :program:`modularize` does to just the module
+map coverage check, use the ``-coverage-check-only option``.
+
+For example::
+
+ modularize -coverage-check-only module.modulemap
+
+.. _module-map-generation:
+
+Module Map Generation
+=====================
+
+If you specify the ``-module-map-path=<module map file>``,
+:program:`modularize` will output a module map based on the input header list.
+A module will be created for each header. Also, if the header in the header
+list is a partial path, a nested module hierarchy will be created in which a
+module will be created for each subdirectory component in the header path,
+with the header itself represented by the innermost module. If other headers
+use the same subdirectories, they will be enclosed in these same modules also.
+
+For example, for the header list::
+
+ SomeTypes.h
+ SomeDecls.h
+ SubModule1/Header1.h
+ SubModule1/Header2.h
+ SubModule2/Header3.h
+ SubModule2/Header4.h
+ SubModule2.h
+
+The following module map will be generated::
+
+ // Output/NoProblemsAssistant.txt\r
+ // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
+ -root-module=Root NoProblemsAssistant.modularize\r
+ \r
+ module SomeTypes {\r
+ header "SomeTypes.h"\r
+ export *\r
+ }\r
+ module SomeDecls {\r
+ header "SomeDecls.h"\r
+ export *\r
+ }\r
+ module SubModule1 {\r
+ module Header1 {\r
+ header "SubModule1/Header1.h"\r
+ export *\r
+ }\r
+ module Header2 {\r
+ header "SubModule1/Header2.h"\r
+ export *\r
+ }\r
+ }\r
+ module SubModule2 {\r
+ module Header3 {\r
+ header "SubModule2/Header3.h"\r
+ export *\r
+ }\r
+ module Header4 {\r
+ header "SubModule2/Header4.h"\r
+ export *\r
+ }\r
+ header "SubModule2.h"\r
+ export *\r
+ }\r
+
+An optional ``-root-module=<root-name>`` option can be used to cause a root module
+to be created which encloses all the modules.
+
+An optional ``-problem-files-list=<problem-file-name>`` can be used to input
+a list of files to be excluded, perhaps as a temporary stop-gap measure until
+problem headers can be fixed.
+
+For example, with the same header list from above::
+
+ // Output/NoProblemsAssistant.txt\r
+ // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
+ -root-module=Root NoProblemsAssistant.modularize\r
+ \r
+ module Root {\r
+ module SomeTypes {\r
+ header "SomeTypes.h"\r
+ export *\r
+ }\r
+ module SomeDecls {\r
+ header "SomeDecls.h"\r
+ export *\r
+ }\r
+ module SubModule1 {\r
+ module Header1 {\r
+ header "SubModule1/Header1.h"\r
+ export *\r
+ }\r
+ module Header2 {\r
+ header "SubModule1/Header2.h"\r
+ export *\r
+ }\r
+ }\r
+ module SubModule2 {\r
+ module Header3 {\r
+ header "SubModule2/Header3.h"\r
+ export *\r
+ }\r
+ module Header4 {\r
+ header "SubModule2/Header4.h"\r
+ export *\r
+ }\r
+ header "SubModule2.h"\r
+ export *\r
+ }\r
+ }\r
+\r
+Note that headers with dependents will be ignored with a warning, as the\r
+Clang module mechanism doesn't support headers the rely on other headers\r
+to be included first.\r
+\r
+The module map format defines some keywords which can't be used in module\r
+names. If a header has one of these names, an underscore ('_') will be\r
+prepended to the name. For example, if the header name is ``header.h``,
+because ``header`` is a keyword, the module name will be ``_header``.
+For a list of the module map keywords, please see:
+`Lexical structure <http://clang.llvm.org/docs/Modules.html#lexical-structure>`_\r
--- /dev/null
+.. index:: pp-trace
+
+==================================
+pp-trace User's Manual
+==================================
+
+.. toctree::
+ :hidden:
+
+:program:`pp-trace` is a standalone tool that traces preprocessor
+activity. It's also used as a test of Clang's PPCallbacks interface.
+It runs a given source file through the Clang preprocessor, displaying
+selected information from callback functions overridden in a
+`PPCallbacks <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html>`_
+derivation. The output is in a high-level YAML format, described in
+:ref:`OutputFormat`.
+
+.. _Usage:
+
+pp-trace Usage
+==============
+
+Command Line Format
+-------------------
+
+``pp-trace [<pp-trace-options>] <source-file> [<front-end-options>]``
+
+``<pp-trace-options>`` is a place-holder for options
+specific to pp-trace, which are described below in
+:ref:`CommandLineOptions`.
+
+``<source-file>`` specifies the source file to run through the preprocessor.
+
+``<front-end-options>`` is a place-holder for regular
+`Clang Compiler Options <http://clang.llvm.org/docs/UsersManual.html#command-line-options>`_,
+which must follow the <source-file>.
+
+.. _CommandLineOptions:
+
+Command Line Options
+--------------------
+
+.. option:: -ignore <callback-name-list>
+
+ This option specifies a comma-separated list of names of callbacks
+ that shouldn't be traced. It can be used to eliminate unwanted
+ trace output. The callback names are the name of the actual
+ callback function names in the PPCallbacks class:
+
+ * FileChanged
+ * FileSkipped
+ * FileNotFound
+ * InclusionDirective
+ * moduleImport
+ * EndOfMainFile
+ * Ident
+ * PragmaDirective
+ * PragmaComment
+ * PragmaDetectMismatch
+ * PragmaDebug
+ * PragmaMessage
+ * PragmaDiagnosticPush
+ * PragmaDiagnosticPop
+ * PragmaDiagnostic
+ * PragmaOpenCLExtension
+ * PragmaWarning
+ * PragmaWarningPush
+ * PragmaWarningPop
+ * MacroExpands
+ * MacroDefined
+ * MacroUndefined
+ * Defined
+ * SourceRangeSkipped
+ * If
+ * Elif
+ * Ifdef
+ * Ifndef
+ * Else
+ * Endif
+
+.. option:: -output <output-file>
+
+ By default, pp-trace outputs the trace information to stdout. Use this
+ option to output the trace information to a file.
+
+.. _OutputFormat:
+
+pp-trace Output Format
+======================
+
+The pp-trace output is formatted as YAML. See http://yaml.org/ for general
+YAML information. It's arranged as a sequence of information about the
+callback call, including the callback name and argument information, for
+example:::
+
+ ---
+ - Callback: Name
+ Argument1: Value1
+ Argument2: Value2
+ (etc.)
+ ...
+
+With real data:::
+
+ ---
+ - Callback: FileChanged\r
+ Loc: "c:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:1:1"\r
+ Reason: EnterFile\r
+ FileType: C_User\r
+ PrevFID: (invalid)\r
+ (etc.)\r
+ - Callback: FileChanged\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:5:1"\r
+ Reason: ExitFile\r
+ FileType: C_User\r
+ PrevFID: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/Input/Level1B.h"\r
+ - Callback: EndOfMainFile\r
+ ...
+
+In all but one case (MacroDirective) the "Argument" scalars have the same
+name as the argument in the corresponding PPCallbacks callback function.
+
+Callback Details
+----------------
+
+The following sections describe the pupose and output format for each callback.
+
+Click on the callback name in the section heading to see the Doxygen
+documentation for the callback.
+
+The argument descriptions table describes the callback argument information
+displayed.
+
+The Argument Name field in most (but not all) cases is the same name as the
+callback function parameter.
+
+The Argument Value Syntax field describes the values that will be displayed
+for the argument value. It uses an ad hoc representation that mixes literal
+and symbolic representations. Enumeration member symbols are shown as the
+actual enum member in a (member1|member2|...) form. A name in parentheses
+can either represent a place holder for the described value, or confusingly,
+it might be a literal, such as (null), for a null pointer.
+Locations are shown as quoted only to avoid confusing the documentation generator.
+
+The Clang C++ Type field is the type from the callback function declaration.
+
+The description describes the argument or what is displayed for it.
+
+Note that in some cases, such as when a structure pointer is an argument
+value, only some key member or members are shown to represent the value,
+instead of trying to display all members of the structure.
+
+`FileChanged <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a7cc8cfaf34114fc65e92af621cd6464e>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileChanged is called when the preprocessor enters or exits a file, both the
+top level file being compiled, as well as any #include directives. It will
+also be called as a result of a system header pragma or in internal renaming
+of a file.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Reason (EnterFile|ExitFile|SystemHeaderPragma|RenameFile) PPCallbacks::FileChangeReason Reason for change.
+FileType (C_User|C_System|C_ExternCSystem) SrcMgr::CharacteristicKind Include type.
+PrevFID ((file)|(invalid)) FileID Previous file, if any.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: FileChanged\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:1:1"\r
+ Reason: EnterFile\r
+ FileType: C_User\r
+ PrevFID: (invalid)\r
+
+`FileSkipped <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab5b338a0670188eb05fa7685bbfb5128>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileSkipped is called when a source file is skipped as the result of header
+guard optimization.
+
+Argument descriptions:
+
+============== ================================================== ============================== ========================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ========================================================
+ParentFile ("(file)" or (null)) const FileEntry The file that #included the skipped file.
+FilenameTok (token) const Token The token in ParentFile that indicates the skipped file.
+FileType (C_User|C_System|C_ExternCSystem) SrcMgr::CharacteristicKind The file type.
+============== ================================================== ============================== ========================================================
+
+Example:::
+
+ - Callback: FileSkipped\r
+ ParentFile: "/path/filename.h"\r
+ FilenameTok: "filename.h"\r
+ FileType: C_User\r
+
+`FileNotFound <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3045151545f987256bfa8d978916ef00>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileNotFound is called when an inclusion directive results in a file-not-found error.
+
+Argument descriptions:
+
+============== ================================================== ============================== =====================================================================================================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== =====================================================================================================================================
+FileName "(file)" StringRef The name of the file being included, as written in the source code.
+RecoveryPath (path) SmallVectorImpl<char> If this client indicates that it can recover from this missing file, the client should set this as an additional header search patch.
+============== ================================================== ============================== =====================================================================================================================================
+
+Example:::
+
+ - Callback: FileNotFound\r
+ FileName: "/path/filename.h"
+ RecoveryPath:
+
+`InclusionDirective <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a557d9738c329793513a6f57d6b60de52>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+InclusionDirective is called when an inclusion directive of any kind (#include</code>, #import</code>, etc.) has been processed, regardless of whether the inclusion will actually result in an inclusion.
+
+Argument descriptions:
+
+============== ================================================== ============================== ============================================================================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ============================================================================================================
+HashLoc "(file):(line):(col)" SourceLocation The location of the '#' that starts the inclusion directive.
+IncludeTok (token) const Token The token that indicates the kind of inclusion directive, e.g., 'include' or 'import'.
+FileName "(file)" StringRef The name of the file being included, as written in the source code.
+IsAngled (true|false) bool Whether the file name was enclosed in angle brackets; otherwise, it was enclosed in quotes.
+FilenameRange "(file)" CharSourceRange The character range of the quotes or angle brackets for the written file name.
+File "(file)" const FileEntry The actual file that may be included by this inclusion directive.
+SearchPath "(path)" StringRef Contains the search path which was used to find the file in the file system.
+RelativePath "(path)" StringRef The path relative to SearchPath, at which the include file was found.
+Imported ((module name)|(null)) const Module The module, whenever an inclusion directive was automatically turned into a module import or null otherwise.
+============== ================================================== ============================== ============================================================================================================
+
+Example:::
+
+ - Callback: InclusionDirective\r
+ IncludeTok: include\r
+ FileName: "Input/Level1B.h"\r
+ IsAngled: false\r
+ FilenameRange: "Input/Level1B.h"\r
+ File: "D:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/Input/Level1B.h"\r
+ SearchPath: "D:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace"\r
+ RelativePath: "Input/Level1B.h"\r
+ Imported: (null)\r
+
+`moduleImport <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#af32dcf1b8b7c179c7fcd3e24e89830fe>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+moduleImport is called when there was an explicit module-import syntax.
+
+Argument descriptions:
+
+============== ================================================== ============================== ===========================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ===========================================================
+ImportLoc "(file):(line):(col)" SourceLocation The location of import directive token.
+Path "(path)" ModuleIdPath The identifiers (and their locations) of the module "path".
+Imported ((module name)|(null)) const Module The imported module; can be null if importing failed.
+============== ================================================== ============================== ===========================================================
+
+Example:::
+
+ - Callback: moduleImport\r
+ ImportLoc: "d:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:2"
+ Path: [{Name: Level1B, Loc: "d:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:9"}, {Name: Level2B, Loc: "d:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:17"}]
+ Imported: Level2B
+
+`EndOfMainFile <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a63e170d069e99bc1c9c7ea0f3bed8bcc>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+EndOfMainFile is called when the end of the main file is reached.
+
+Argument descriptions:
+
+============== ================================================== ============================== ======================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ======================
+(no arguments)
+============== ================================================== ============================== ======================
+
+Example:::
+
+ - Callback: EndOfMainFile\r
+
+`Ident <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3683f1d1fa513e9b6193d446a5cc2b66>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ident is called when a #ident or #sccs directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+str (name) const std::string The text of the directive.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: Ident\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-ident.cpp:3:1"
+ str: "$Id$"
+
+`PragmaDirective <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0a2d7a72c62184b3cbde31fb62c6f2f7>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDirective is called when start reading any pragma directive.
+
+Argument descriptions:
+
+============== ================================================== ============================== =================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== =================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Introducer (PIK_HashPragma|PIK__Pragma|PIK___pragma) PragmaIntroducerKind The type of the pragma directive.
+============== ================================================== ============================== =================================
+
+Example:::
+
+ - Callback: PragmaDirective\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Introducer: PIK_HashPragma
+
+`PragmaComment <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ace0d940fc2c12ab76441466aab58dc37>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaComment is called when a #pragma comment directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Kind ((name)|(null)) const IdentifierInfo The comment kind symbol.
+Str (message directive) const std::string The comment message directive.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaComment\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Kind: library
+ Str: kernel32.lib
+
+`PragmaDetectMismatch <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab11158c9149fb8ad8af1903f4a6cd65d>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDetectMismatch is called when a #pragma detect_mismatch directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Name "(name)" const std::string The name.
+Value (string) const std::string The value.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaDetectMismatch\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Name: name
+ Value: value
+
+`PragmaDebug <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a57cdccb6dcc07e926513ac3d5b121466>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDebug is called when a #pragma clang __debug directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+DebugType (string) StringRef Indicates type of debug message.
+============== ================================================== ============================== ================================
+
+Example:::
+
+ - Callback: PragmaDebug\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ DebugType: warning
+
+`PragmaMessage <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abb42935d9a9fd8e2c4f51cfdc4ea2ae1>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaMessage is called when a #pragma message directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== =======================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== =======================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Namespace (name) StringRef The namespace of the message directive.
+Kind (PMK_Message|PMK_Warning|PMK_Error) PPCallbacks::PragmaMessageKind The type of the message directive.
+Str (string) StringRef The text of the message directive.
+============== ================================================== ============================== =======================================
+
+Example:::
+
+ - Callback: PragmaMessage\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Namespace: "GCC"
+ Kind: PMK_Message
+ Str: The message text.
+
+`PragmaDiagnosticPush <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0f3ff19762baa38fe6c5c58022d32979>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnosticPush is called when a #pragma gcc dianostic push directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Namespace (name) StringRef Namespace name.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaDiagnosticPush\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Namespace: "GCC"
+
+`PragmaDiagnosticPop <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac94d789873122221fba8d76f6c5ea45e>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnosticPop is called when a #pragma gcc dianostic pop directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Namespace (name) StringRef Namespace name.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaDiagnosticPop\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Namespace: "GCC"
+
+`PragmaDiagnostic <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afe7938f38a83cb7b4b25a13edfdd7bdd>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnostic is called when a #pragma gcc dianostic directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Namespace (name) StringRef Namespace name.
+mapping (0|MAP_IGNORE|MAP_WARNING|MAP_ERROR|MAP_FATAL) diag::Severity Mapping type.
+Str (string) StringRef Warning/error name.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaDiagnostic\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Namespace: "GCC"
+ mapping: MAP_WARNING
+ Str: WarningName
+
+`PragmaOpenCLExtension <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a92a20a21fadbab4e2c788f4e27fe07e7>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaOpenCLExtension is called when OpenCL extension is either disabled or enabled with a pragma.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==========================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==========================
+NameLoc "(file):(line):(col)" SourceLocation The location of the name.
+Name (name) const IdentifierInfo Name symbol.
+StateLoc "(file):(line):(col)" SourceLocation The location of the state.
+State (1|0) unsigned Enabled/disabled state.
+============== ================================================== ============================== ==========================
+
+Example:::
+
+ - Callback: PragmaOpenCLExtension\r
+ NameLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:10"
+ Name: Name
+ StateLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:18"
+ State: 1
+
+`PragmaWarning <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#aa17169d25fa1cf0a6992fc944d1d8730>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarning is called when a #pragma warning directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+WarningSpec (string) StringRef The warning specifier.
+Ids [(number)[, ...]] ArrayRef<int> The warning numbers.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaWarning\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ WarningSpec: disable
+ Ids: 1,2,3
+
+`PragmaWarningPush <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ae5626ef70502687a859f323a809ed0b6>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarningPush is called when a #pragma warning(push) directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+Level (number) int Warning level.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaWarningPush\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+ Level: 1
+
+`PragmaWarningPop <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac98d502af8811b8a6e7342d7cd2b3b95>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarningPop is called when a #pragma warning(pop) directive is read.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+============== ================================================== ============================== ==============================
+
+Example:::
+
+ - Callback: PragmaWarningPop\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+
+`MacroExpands <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a9bc725209d3a071ea649144ab996d515>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroExpands is called when ::HandleMacroExpandedIdentifier when a macro invocation is found.
+
+Argument descriptions:
+
+============== ================================================== ============================== ======================================================================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ======================================================================================================
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+Range ["(file):(line):(col)", "(file):(line):(col)"] SourceRange The source range for the expansion.
+Args [(name)|(number)|<(token name)>[, ...]] const MacroArgs The argument tokens. Names and numbers are literal, everything else is of the form '<' tokenName '>'.
+============== ================================================== ============================== ======================================================================================================
+
+Example:::
+
+ - Callback: MacroExpands\r
+ MacroNameTok: X_IMPL\r
+ MacroDirective: MD_Define\r
+ Range: [(nonfile), (nonfile)]\r
+ Args: [a <plus> y, b]\r
+
+`MacroDefined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a8448fc9f96f22ad1b93ff393cffc5a76>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroDefined is called when a macro definition is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================================================
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+============== ================================================== ============================== ==============================================================
+
+Example:::
+
+ - Callback: MacroDefined\r
+ MacroNameTok: X_IMPL\r
+ MacroDirective: MD_Define\r
+
+`MacroUndefined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#acb80fc6171a839db8e290945bf2c9d7a>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroUndefined is called when a macro #undef is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================================================
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+============== ================================================== ============================== ==============================================================
+
+Example:::
+
+ - Callback: MacroUndefined\r
+ MacroNameTok: X_IMPL\r
+ MacroDirective: MD_Define\r
+
+`Defined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3cc2a644533d0e4088a13d2baf90db94>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Defined is called when the 'defined' operator is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================================================
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+Range ["(file):(line):(col)", "(file):(line):(col)"] SourceRange The source range for the directive.
+============== ================================================== ============================== ==============================================================
+
+Example:::
+
+ - Callback: Defined\r
+ MacroNameTok: MACRO
+ MacroDirective: (null)
+ Range: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:5", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:19"]
+
+`SourceRangeSkipped <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abdb4ebe11610f079ac33515965794b46>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+SourceRangeSkipped is called when a source range is skipped.
+
+Argument descriptions:
+
+============== ================================================== ============================== =========================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== =========================
+Range ["(file):(line):(col)", "(file):(line):(col)"] SourceRange The source range skipped.
+============== ================================================== ============================== =========================
+
+Example:::
+
+ - Callback: SourceRangeSkipped\r
+ Range: [":/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2", ":/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:9:2"]
+
+`If <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a645edcb0d6becbc6f256f02fd1287778>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If is called when an #if is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ===================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ===================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+ConditionRange ["(file):(line):(col)", "(file):(line):(col)"] SourceRange The source range for the condition.
+ConditionValue (true|false) bool The condition value.
+============== ================================================== ============================== ===================================
+
+Example:::
+
+ - Callback: If\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+ ConditionRange: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:4", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:9:1"]\r
+ ConditionValue: false\r
+
+`Elif <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a180c9e106a28d60a6112e16b1bb8302a>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Elif is called when an #elif is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ===================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ===================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+ConditionRange ["(file):(line):(col)", "(file):(line):(col)"] SourceRange The source range for the condition.
+ConditionValue (true|false) bool The condition value.
+IfLoc "(file):(line):(col)" SourceLocation The location of the directive.
+============== ================================================== ============================== ===================================
+
+Example:::
+
+ - Callback: Elif\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+ ConditionRange: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:4", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:11:1"]\r
+ ConditionValue: false\r
+ IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+`Ifdef <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0ce79575dda307784fd51a6dd4eec33d>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ifdef is called when an #ifdef is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+============== ================================================== ============================== ==============================================================
+
+Example:::
+
+ - Callback: Ifdef\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
+ MacroNameTok: MACRO\r
+ MacroDirective: MD_Define\r
+
+`Ifndef <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a767af69f1cdcc4cd880fa2ebf77ad3ad>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ifndef is called when an #ifndef is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ==============================================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ==============================================================
+Loc "(file):(line):(col)" SourceLocation The location of the directive.
+MacroNameTok (token) const Token The macro name token.
+MacroDirective (MD_Define|MD_Undefine|MD_Visibility) const MacroDirective The kind of macro directive from the MacroDirective structure.
+============== ================================================== ============================== ==============================================================
+
+Example:::
+
+ - Callback: Ifndef\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
+ MacroNameTok: MACRO\r
+ MacroDirective: MD_Define\r
+
+`Else <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ad57f91b6d9c3cbcca326a2bfb49e0314>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Else is called when an #else is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ===================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ===================================
+Loc "(file):(line):(col)" SourceLocation The location of the else directive.
+IfLoc "(file):(line):(col)" SourceLocation The location of the if directive.
+============== ================================================== ============================== ===================================
+
+Example:::
+
+ - Callback: Else\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+ IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+`Endif <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afc62ca1401125f516d58b1629a2093ce>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Endif is called when an #endif is seen.
+
+Argument descriptions:
+
+============== ================================================== ============================== ====================================
+Argument Name Argument Value Syntax Clang C++ Type Description
+============== ================================================== ============================== ====================================
+Loc "(file):(line):(col)" SourceLocation The location of the endif directive.
+IfLoc "(file):(line):(col)" SourceLocation The location of the if directive.
+============== ================================================== ============================== ====================================
+
+Example:::
+
+ - Callback: Endif\r
+ Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+ IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+Building pp-trace
+=================
+
+To build from source:
+
+1. Read `Getting Started with the LLVM System`_ and `Clang Tools
+ Documentation`_ for information on getting sources for LLVM, Clang, and
+ Clang Extra Tools.
+
+2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
+ directions for how to build. With sources all checked out into the
+ right place the LLVM build will build Clang Extra Tools and their
+ dependencies automatically.
+
+ * If using CMake, you can also use the ``pp-trace`` target to build
+ just the pp-trace tool and its dependencies.
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Building LLVM with CMake: http://llvm.org/docs/CMake.html
+.. _Clang Tools Documentation: http://clang.llvm.org/docs/ClangTools.html
+
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_library(clangIncludeFixer
+ IncludeFixer.cpp
+ IncludeFixerContext.cpp
+ InMemorySymbolIndex.cpp
+ FuzzySymbolIndex.cpp
+ SymbolIndexManager.cpp
+ YamlSymbolIndex.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangParse
+ clangSema
+ clangTooling
+ clangToolingCore
+ findAllSymbols
+ )
+
+add_subdirectory(plugin)
+add_subdirectory(tool)
+add_subdirectory(find-all-symbols)
--- /dev/null
+//===--- FuzzySymbolIndex.cpp - Lookup symbols for autocomplete -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "FuzzySymbolIndex.h"
+#include "llvm/Support/Regex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+using llvm::StringRef;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+class MemSymbolIndex : public FuzzySymbolIndex {
+public:
+ MemSymbolIndex(std::vector<SymbolAndSignals> Symbols) {
+ for (auto &Symbol : Symbols) {
+ auto Tokens = tokenize(Symbol.Symbol.getName());
+ this->Symbols.emplace_back(
+ StringRef(llvm::join(Tokens.begin(), Tokens.end(), " ")),
+ std::move(Symbol));
+ }
+ }
+
+ std::vector<SymbolAndSignals> search(StringRef Query) override {
+ auto Tokens = tokenize(Query);
+ llvm::Regex Pattern("^" + queryRegexp(Tokens));
+ std::vector<SymbolAndSignals> Results;
+ for (const Entry &E : Symbols)
+ if (Pattern.match(E.first))
+ Results.push_back(E.second);
+ return Results;
+ }
+
+private:
+ using Entry = std::pair<llvm::SmallString<32>, SymbolAndSignals>;
+ std::vector<Entry> Symbols;
+};
+
+// Helpers for tokenize state machine.
+enum TokenizeState {
+ EMPTY, // No pending characters.
+ ONE_BIG, // Read one uppercase letter, could be WORD or Word.
+ BIG_WORD, // Reading an uppercase WORD.
+ SMALL_WORD, // Reading a lowercase word.
+ NUMBER // Reading a number.
+};
+
+enum CharType { UPPER, LOWER, DIGIT, MISC };
+CharType classify(char c) {
+ if (isupper(c))
+ return UPPER;
+ if (islower(c))
+ return LOWER;
+ if (isdigit(c))
+ return DIGIT;
+ return MISC;
+}
+
+} // namespace
+
+std::vector<std::string> FuzzySymbolIndex::tokenize(StringRef Text) {
+ std::vector<std::string> Result;
+ // State describes the treatment of text from Start to I.
+ // Once text is Flush()ed into Result, we're done with it and advance Start.
+ TokenizeState State = EMPTY;
+ size_t Start = 0;
+ auto Flush = [&](size_t End) {
+ if (State != EMPTY) {
+ Result.push_back(Text.substr(Start, End - Start).lower());
+ State = EMPTY;
+ }
+ Start = End;
+ };
+ for (size_t I = 0; I < Text.size(); ++I) {
+ CharType Type = classify(Text[I]);
+ if (Type == MISC)
+ Flush(I);
+ else if (Type == LOWER)
+ switch (State) {
+ case BIG_WORD:
+ Flush(I - 1); // FOOBar: first token is FOO, not FOOB.
+ LLVM_FALLTHROUGH;
+ case ONE_BIG:
+ State = SMALL_WORD;
+ LLVM_FALLTHROUGH;
+ case SMALL_WORD:
+ break;
+ default:
+ Flush(I);
+ State = SMALL_WORD;
+ }
+ else if (Type == UPPER)
+ switch (State) {
+ case ONE_BIG:
+ State = BIG_WORD;
+ LLVM_FALLTHROUGH;
+ case BIG_WORD:
+ break;
+ default:
+ Flush(I);
+ State = ONE_BIG;
+ }
+ else if (Type == DIGIT && State != NUMBER) {
+ Flush(I);
+ State = NUMBER;
+ }
+ }
+ Flush(Text.size());
+ return Result;
+}
+
+std::string
+FuzzySymbolIndex::queryRegexp(const std::vector<std::string> &Tokens) {
+ std::string Result;
+ for (size_t I = 0; I < Tokens.size(); ++I) {
+ if (I)
+ Result.append("[[:alnum:]]* ");
+ for (size_t J = 0; J < Tokens[I].size(); ++J) {
+ if (J)
+ Result.append("([[:alnum:]]* )?");
+ Result.push_back(Tokens[I][J]);
+ }
+ }
+ return Result;
+}
+
+llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+FuzzySymbolIndex::createFromYAML(StringRef FilePath) {
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer)
+ return llvm::errorCodeToError(Buffer.getError());
+ return llvm::make_unique<MemSymbolIndex>(
+ find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()));
+}
+
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===--- FuzzySymbolIndex.h - Lookup symbols for autocomplete ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+// A FuzzySymbolIndex retrieves top-level symbols matching a query string.
+//
+// It refines the contract of SymbolIndex::search to do fuzzy matching:
+// - symbol names are tokenized: "unique ptr", "string ref".
+// - query must match prefixes of symbol tokens: [upt]
+// - if the query has multiple tokens, splits must match: [StR], not [STr].
+// Helpers for tokenization and regex matching are provided.
+//
+// Implementations may choose to truncate results, refuse short queries, etc.
+class FuzzySymbolIndex : public SymbolIndex {
+public:
+ // Loads the specified include-fixer database and returns an index serving it.
+ static llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+ createFromYAML(llvm::StringRef File);
+
+ // Helpers for implementing indexes:
+
+ // Transforms a symbol name or query into a sequence of tokens.
+ // - URLHandlerCallback --> [url, handler, callback]
+ // - snake_case11 --> [snake, case, 11]
+ // - _WTF$ --> [wtf]
+ static std::vector<std::string> tokenize(llvm::StringRef Text);
+
+ // Transforms query tokens into an unanchored regexp to match symbol tokens.
+ // - [fe f] --> /f(\w* )?e\w* f/, matches [fee fie foe].
+ static std::string queryRegexp(const std::vector<std::string> &Tokens);
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
--- /dev/null
+//===-- InMemorySymbolIndex.cpp--------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemorySymbolIndex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+InMemorySymbolIndex::InMemorySymbolIndex(
+ const std::vector<SymbolAndSignals> &Symbols) {
+ for (const auto &Symbol : Symbols)
+ LookupTable[Symbol.Symbol.getName()].push_back(Symbol);
+}
+
+std::vector<SymbolAndSignals>
+InMemorySymbolIndex::search(llvm::StringRef Identifier) {
+ auto I = LookupTable.find(Identifier);
+ if (I != LookupTable.end())
+ return I->second;
+ return {};
+}
+
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===-- InMemorySymbolIndex.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Xref database with fixed content.
+class InMemorySymbolIndex : public SymbolIndex {
+public:
+ InMemorySymbolIndex(
+ const std::vector<find_all_symbols::SymbolAndSignals> &Symbols);
+
+ std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) override;
+
+private:
+ std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
+ LookupTable;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
--- /dev/null
+//===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixer.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "include-fixer"
+
+using namespace clang;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+/// Manages the parse, gathers include suggestions.
+class Action : public clang::ASTFrontendAction {
+public:
+ explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
+ : SemaSource(SymbolIndexMgr, MinimizeIncludePaths,
+ /*GenerateDiagnostics=*/false) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ StringRef InFile) override {
+ SemaSource.setFilePath(InFile);
+ return llvm::make_unique<clang::ASTConsumer>();
+ }
+
+ void ExecuteAction() override {
+ clang::CompilerInstance *Compiler = &getCompilerInstance();
+ assert(!Compiler->hasSema() && "CI already has Sema");
+
+ // Set up our hooks into sema and parse the AST.
+ if (hasCodeCompletionSupport() &&
+ !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty())
+ Compiler->createCodeCompletionConsumer();
+
+ clang::CodeCompleteConsumer *CompletionConsumer = nullptr;
+ if (Compiler->hasCodeCompletionConsumer())
+ CompletionConsumer = &Compiler->getCodeCompletionConsumer();
+
+ Compiler->createSema(getTranslationUnitKind(), CompletionConsumer);
+ SemaSource.setCompilerInstance(Compiler);
+ Compiler->getSema().addExternalSource(&SemaSource);
+
+ clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
+ Compiler->getFrontendOpts().SkipFunctionBodies);
+ }
+
+ IncludeFixerContext
+ getIncludeFixerContext(const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ return SemaSource.getIncludeFixerContext(SourceManager, HeaderSearch,
+ SemaSource.getMatchedSymbols());
+ }
+
+private:
+ IncludeFixerSemaSource SemaSource;
+};
+
+} // namespace
+
+IncludeFixerActionFactory::IncludeFixerActionFactory(
+ SymbolIndexManager &SymbolIndexMgr,
+ std::vector<IncludeFixerContext> &Contexts, StringRef StyleName,
+ bool MinimizeIncludePaths)
+ : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts),
+ MinimizeIncludePaths(MinimizeIncludePaths) {}
+
+IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
+
+bool IncludeFixerActionFactory::runInvocation(
+ std::shared_ptr<clang::CompilerInvocation> Invocation,
+ clang::FileManager *Files,
+ std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
+ clang::DiagnosticConsumer *Diagnostics) {
+ assert(Invocation->getFrontendOpts().Inputs.size() == 1);
+
+ // Set up Clang.
+ clang::CompilerInstance Compiler(PCHContainerOps);
+ Compiler.setInvocation(std::move(Invocation));
+ Compiler.setFileManager(Files);
+
+ // Create the compiler's actual diagnostics engine. We want to drop all
+ // diagnostics here.
+ Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
+ /*ShouldOwnClient=*/true);
+ Compiler.createSourceManager(*Files);
+
+ // We abort on fatal errors so don't let a large number of errors become
+ // fatal. A missing #include can cause thousands of errors.
+ Compiler.getDiagnostics().setErrorLimit(0);
+
+ // Run the parser, gather missing includes.
+ auto ScopedToolAction =
+ llvm::make_unique<Action>(SymbolIndexMgr, MinimizeIncludePaths);
+ Compiler.ExecuteAction(*ScopedToolAction);
+
+ Contexts.push_back(ScopedToolAction->getIncludeFixerContext(
+ Compiler.getSourceManager(),
+ 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
+ // errors.
+ return !Compiler.getDiagnostics().hasFatalErrorOccurred();
+}
+
+static bool addDiagnosticsForContext(TypoCorrection &Correction,
+ const IncludeFixerContext &Context,
+ StringRef Code, SourceLocation StartOfFile,
+ ASTContext &Ctx) {
+ auto Reps = createIncludeFixerReplacements(
+ Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false);
+ if (!Reps || Reps->size() != 1)
+ return false;
+
+ unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
+ DiagnosticsEngine::Note, "Add '#include %0' to provide the missing "
+ "declaration [clang-include-fixer]");
+
+ // FIXME: Currently we only generate a diagnostic for the first header. Give
+ // the user choices.
+ const tooling::Replacement &Placed = *Reps->begin();
+
+ auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset());
+ auto End = Begin.getLocWithOffset(std::max(0, (int)Placed.getLength() - 1));
+ PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator());
+ PD << Context.getHeaderInfos().front().Header
+ << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Begin, End),
+ Placed.getReplacementText());
+ Correction.addExtraDiagnostic(std::move(PD));
+ return true;
+}
+
+/// Callback for incomplete types. If we encounter a forward declaration we
+/// have the fully qualified name ready. Just query that.
+bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType(
+ clang::SourceLocation Loc, clang::QualType T) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return false;
+
+ clang::ASTContext &context = CI->getASTContext();
+ std::string QueryString = QualType(T->getUnqualifiedDesugaredType(), 0)
+ .getAsString(context.getPrintingPolicy());
+ DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString << "'");
+ // Pass an empty range here since we don't add qualifier in this case.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ query(QueryString, "", tooling::Range());
+
+ if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+ TypoCorrection Correction;
+ FileID FID = CI->getSourceManager().getFileID(Loc);
+ StringRef Code = CI->getSourceManager().getBufferData(FID);
+ SourceLocation StartOfFile =
+ CI->getSourceManager().getLocForStartOfFile(FID);
+ addDiagnosticsForContext(
+ Correction,
+ getIncludeFixerContext(CI->getSourceManager(),
+ CI->getPreprocessor().getHeaderSearchInfo(),
+ MatchedSymbols),
+ Code, StartOfFile, CI->getASTContext());
+ for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
+ CI->getSema().Diag(Loc, PD);
+ }
+ return true;
+}
+
+/// Callback for unknown identifiers. Try to piece together as much
+/// qualification as we can get and do a query.
+clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo(
+ const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC, DeclContext *MemberContext,
+ bool EnteringContext, const ObjCObjectPointerType *OPT) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return clang::TypoCorrection();
+
+ // We currently ignore the unidentified symbol which is not from the
+ // main file.
+ //
+ // However, this is not always true due to templates in a non-self contained
+ // header, consider the case:
+ //
+ // // header.h
+ // template <typename T>
+ // class Foo {
+ // T t;
+ // };
+ //
+ // // test.cc
+ // // We need to add <bar.h> in test.cc instead of header.h.
+ // class Bar;
+ // Foo<Bar> foo;
+ //
+ // FIXME: Add the missing header to the header file where the symbol comes
+ // from.
+ if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc()))
+ return clang::TypoCorrection();
+
+ std::string TypoScopeString;
+ if (S) {
+ // FIXME: Currently we only use namespace contexts. Use other context
+ // types for query.
+ for (const auto *Context = S->getEntity(); Context;
+ Context = Context->getParent()) {
+ if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
+ if (!ND->getName().empty())
+ TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
+ }
+ }
+ }
+
+ auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
+ StringRef Source =
+ Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts());
+
+ // Skip forward until we find a character that's neither identifier nor
+ // colon. This is a bit of a hack around the fact that we will only get a
+ // single callback for a long nested name if a part of the beginning is
+ // unknown. For example:
+ //
+ // llvm::sys::path::parent_path(...)
+ // ^~~~ ^~~
+ // known
+ // ^~~~
+ // unknown, last callback
+ // ^~~~~~~~~~~
+ // no callback
+ //
+ // With the extension we get the full nested name specifier including
+ // parent_path.
+ // FIXME: Don't rely on source text.
+ const char *End = Source.end();
+ while (isIdentifierBody(*End) || *End == ':')
+ ++End;
+
+ return std::string(Source.begin(), End);
+ };
+
+ /// If we have a scope specification, use that to get more precise results.
+ std::string QueryString;
+ tooling::Range SymbolRange;
+ const auto &SM = CI->getSourceManager();
+ auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
+ return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
+ QueryString.size());
+ };
+ if (SS && SS->getRange().isValid()) {
+ auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
+ Typo.getLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
+ auto Range =
+ CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else {
+ QueryString = Typo.getAsString();
+ SymbolRange = CreateToolingRange(Typo.getLoc());
+ }
+
+ DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString << "\n");
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ query(QueryString, TypoScopeString, SymbolRange);
+
+ if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+ TypoCorrection Correction(Typo.getName());
+ Correction.setCorrectionRange(SS, Typo);
+ FileID FID = SM.getFileID(Typo.getLoc());
+ StringRef Code = SM.getBufferData(FID);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ if (addDiagnosticsForContext(
+ Correction, getIncludeFixerContext(
+ SM, CI->getPreprocessor().getHeaderSearchInfo(),
+ MatchedSymbols),
+ Code, StartOfFile, CI->getASTContext()))
+ return Correction;
+ }
+ return TypoCorrection();
+}
+
+/// Get the minimal include for a given path.
+std::string IncludeFixerSemaSource::minimizeInclude(
+ StringRef Include, const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ if (!MinimizeIncludePaths)
+ return Include;
+
+ // Get the FileEntry for the include.
+ StringRef StrippedInclude = Include.trim("\"<>");
+ const FileEntry *Entry =
+ SourceManager.getFileManager().getFile(StrippedInclude);
+
+ // If the file doesn't exist return the path from the database.
+ // FIXME: This should never happen.
+ if (!Entry)
+ return Include;
+
+ bool IsSystem;
+ std::string Suggestion =
+ HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
+
+ return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
+}
+
+/// Get the include fixer context for the queried symbol.
+IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch,
+ ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const {
+ std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
+ for (const auto &Symbol : MatchedSymbols) {
+ std::string FilePath = Symbol.getFilePath().str();
+ std::string MinimizedFilePath = minimizeInclude(
+ ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
+ : "\"" + FilePath + "\""),
+ SourceManager, HeaderSearch);
+ SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
+ MinimizedFilePath, Symbol.getContexts());
+ }
+ return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers,
+ tooling::Range Range) {
+ assert(!Query.empty() && "Empty query!");
+
+ // Save all instances of an unidentified symbol.
+ //
+ // We use conservative behavior for detecting the same unidentified symbol
+ // here. The symbols which have the same ScopedQualifier and RawIdentifier
+ // are considered equal. So that include-fixer avoids false positives, and
+ // always adds missing qualifiers to correct symbols.
+ if (!GenerateDiagnostics && !QuerySymbolInfos.empty()) {
+ if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
+ Query == QuerySymbolInfos.front().RawIdentifier) {
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+ }
+ return {};
+ }
+
+ DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
+ DEBUG(CI->getSourceManager()
+ .getLocForStartOfFile(CI->getSourceManager().getMainFileID())
+ .getLocWithOffset(Range.getOffset())
+ .print(llvm::dbgs(), CI->getSourceManager()));
+ DEBUG(llvm::dbgs() << " ...");
+ llvm::StringRef FileName = CI->getSourceManager().getFilename(
+ CI->getSourceManager().getLocForStartOfFile(
+ CI->getSourceManager().getMainFileID()));
+
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+
+ // Query the symbol based on C++ name Lookup rules.
+ // Firstly, lookup the identifier with scoped namespace contexts;
+ // If that fails, falls back to look up the identifier directly.
+ //
+ // For example:
+ //
+ // namespace a {
+ // b::foo f;
+ // }
+ //
+ // 1. lookup a::b::foo.
+ // 2. lookup b::foo.
+ std::string QueryString = ScopedQualifiers.str() + Query.str();
+ // 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.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+ SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false, FileName);
+ if (MatchedSymbols.empty())
+ MatchedSymbols =
+ SymbolIndexMgr.search(Query, /*IsNestedSearch=*/true, FileName);
+ DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
+ << " symbols\n");
+ // We store a copy of MatchedSymbols in a place where it's globally reachable.
+ // This is used by the standalone version of the tool.
+ this->MatchedSymbols = MatchedSymbols;
+ return MatchedSymbols;
+}
+
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+ 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;
+ 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) {
+ 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, Replaces, Style);
+}
+
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include <memory>
+#include <vector>
+
+namespace clang {
+
+class CompilerInvocation;
+class DiagnosticConsumer;
+class FileManager;
+class PCHContainerOperations;
+
+namespace include_fixer {
+
+class IncludeFixerActionFactory : public clang::tooling::ToolAction {
+public:
+ /// \param SymbolIndexMgr A source for matching symbols to header files.
+ /// \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,
+ std::vector<IncludeFixerContext> &Contexts,
+ StringRef StyleName,
+ bool MinimizeIncludePaths = true);
+
+ ~IncludeFixerActionFactory() override;
+
+ bool
+ runInvocation(std::shared_ptr<clang::CompilerInvocation> Invocation,
+ clang::FileManager *Files,
+ std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
+ clang::DiagnosticConsumer *Diagnostics) override;
+
+private:
+ /// The client to use to find cross-references.
+ SymbolIndexManager &SymbolIndexMgr;
+
+ /// Multiple contexts for files being processed.
+ std::vector<IncludeFixerContext> &Contexts;
+
+ /// Whether inserted include paths should be optimized.
+ bool MinimizeIncludePaths;
+
+ /// The fallback format style for formatting after insertion if no
+ /// clang-format config file was found.
+ std::string FallbackStyle;
+};
+
+/// Create replacements, which are generated by clang-format, for the
+/// missing header and mising qualifiers insertions. The function uses the
+/// first header for insertion.
+///
+/// \param Code The source code.
+/// \param Context The context which contains all information for creating
+/// include-fixer replacements.
+/// \param Style clang-format style being used.
+/// \param AddQualifiers Whether we should add qualifiers to all instances of
+/// an unidentified symbol.
+///
+/// \return Formatted replacements for inserting, sorting headers and adding
+/// qualifiers on success; otherwise, an llvm::Error carrying llvm::StringError
+/// is returned.
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+ StringRef Code, const IncludeFixerContext &Context,
+ const format::FormatStyle &Style = format::getLLVMStyle(),
+ bool AddQualifiers = true);
+
+/// Handles callbacks from sema, does the include lookup and turns it into an
+/// IncludeFixerContext.
+class IncludeFixerSemaSource : public clang::ExternalSemaSource {
+public:
+ explicit IncludeFixerSemaSource(SymbolIndexManager &SymbolIndexMgr,
+ bool MinimizeIncludePaths,
+ bool GenerateDiagnostics)
+ : SymbolIndexMgr(SymbolIndexMgr),
+ MinimizeIncludePaths(MinimizeIncludePaths),
+ GenerateDiagnostics(GenerateDiagnostics) {}
+
+ void setCompilerInstance(CompilerInstance *CI) { this->CI = CI; }
+ void setFilePath(StringRef FilePath) { this->FilePath = FilePath; }
+
+ /// Callback for incomplete types. If we encounter a forward declaration we
+ /// have the fully qualified name ready. Just query that.
+ bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+ clang::QualType T) override;
+
+ /// Callback for unknown identifiers. Try to piece together as much
+ /// qualification as we can get and do a query.
+ clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+ int LookupKind, Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC,
+ DeclContext *MemberContext,
+ bool EnteringContext,
+ const ObjCObjectPointerType *OPT) override;
+
+ /// Get the minimal include for a given path.
+ std::string minimizeInclude(StringRef Include,
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const;
+
+ /// Get the include fixer context for the queried symbol.
+ IncludeFixerContext getIncludeFixerContext(
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch,
+ ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const;
+
+ /// Get the global matched symbols.
+ ArrayRef<find_all_symbols::SymbolInfo> getMatchedSymbols() const {
+ return MatchedSymbols;
+ }
+
+private:
+ /// Query the database for a given identifier.
+ std::vector<find_all_symbols::SymbolInfo>
+ query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range);
+
+ CompilerInstance *CI;
+
+ /// The client to use to find cross-references.
+ SymbolIndexManager &SymbolIndexMgr;
+
+ /// The information of the symbols being queried.
+ std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
+
+ /// All symbol candidates which match QuerySymbol. We only include the first
+ /// discovered identifier to avoid getting caught in results from error
+ /// recovery.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+ /// The file path to the file being processed.
+ std::string FilePath;
+
+ /// Whether we should use the smallest possible include path.
+ bool MinimizeIncludePaths = true;
+
+ /// Whether we should generate diagnostics with fixits for missing symbols.
+ bool GenerateDiagnostics = false;
+};
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
--- /dev/null
+//===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixerContext.h"
+#include <algorithm>
+
+namespace clang {
+namespace include_fixer {
+
+namespace {
+
+// Splits a multiply qualified names (e.g. a::b::c).
+llvm::SmallVector<llvm::StringRef, 8>
+SplitQualifiers(llvm::StringRef StringQualifiers) {
+ llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
+ StringQualifiers.split(Qualifiers, "::");
+ return Qualifiers;
+}
+
+std::string createQualifiedNameForReplacement(
+ llvm::StringRef RawSymbolName,
+ llvm::StringRef SymbolScopedQualifiersName,
+ const find_all_symbols::SymbolInfo &MatchedSymbol) {
+ // No need to add missing qualifiers if SymbolIndentifer has a global scope
+ // operator "::".
+ if (RawSymbolName.startswith("::"))
+ return RawSymbolName;
+
+ std::string QualifiedName = MatchedSymbol.getQualifiedName();
+
+ // For nested classes, the qualified name constructed from database misses
+ // some stripped qualifiers, because when we search a symbol in database,
+ // we strip qualifiers from the end until we find a result. So append the
+ // missing stripped qualifiers here.
+ //
+ // Get stripped qualifiers.
+ auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
+ std::string StrippedQualifiers;
+ while (!SymbolQualifiers.empty() &&
+ !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
+ StrippedQualifiers =
+ "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
+ SymbolQualifiers.pop_back();
+ }
+ // Append the missing stripped qualifiers.
+ std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
+
+ // Try to find and skip the common prefix qualifiers.
+ auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName);
+ auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName);
+ auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin();
+ auto SymbolScopedQualifiersIter = ScopedQualifiers.begin();
+ while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() &&
+ SymbolScopedQualifiersIter != ScopedQualifiers.end()) {
+ if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter)
+ break;
+ ++FullySymbolQualifiersIter;
+ ++SymbolScopedQualifiersIter;
+ }
+ std::string Result;
+ for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
+ ++FullySymbolQualifiersIter) {
+ if (!Result.empty())
+ Result += "::";
+ Result += *FullySymbolQualifiersIter;
+ }
+ return Result;
+}
+
+} // anonymous namespace
+
+IncludeFixerContext::IncludeFixerContext(
+ StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols,
+ std::vector<find_all_symbols::SymbolInfo> Symbols)
+ : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)),
+ MatchedSymbols(std::move(Symbols)) {
+ // Remove replicated QuerySymbolInfos with the same range.
+ //
+ // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
+ // callback doesn't always work as we expected. In somecases, it will be
+ // triggered at the same position or unidentified symbol multiple times.
+ std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+ [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+ return std::make_pair(A.Range.getOffset(), A.Range.getLength()) <
+ std::make_pair(B.Range.getOffset(), B.Range.getLength());
+ });
+ QuerySymbolInfos.erase(
+ std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+ [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+ return A.Range == B.Range;
+ }),
+ QuerySymbolInfos.end());
+ for (const auto &Symbol : MatchedSymbols) {
+ HeaderInfos.push_back(
+ {Symbol.getFilePath().str(),
+ createQualifiedNameForReplacement(
+ QuerySymbolInfos.front().RawIdentifier,
+ QuerySymbolInfos.front().ScopedQualifiers, Symbol)});
+ }
+ // Deduplicate header infos.
+ HeaderInfos.erase(std::unique(HeaderInfos.begin(), HeaderInfos.end(),
+ [](const HeaderInfo &A, const HeaderInfo &B) {
+ return A.Header == B.Header &&
+ A.QualifiedName == B.QualifiedName;
+ }),
+ HeaderInfos.end());
+}
+
+} // include_fixer
+} // clang
--- /dev/null
+//===-- IncludeFixerContext.h - Include fixer context -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// \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 {
+ /// \brief The header where QualifiedName comes from.
+ std::string Header;
+ /// \brief A symbol name with completed namespace qualifiers which will
+ /// replace the original symbol.
+ std::string QualifiedName;
+ };
+
+ struct QuerySymbolInfo {
+ /// \brief The raw symbol name being queried in database. This name might
+ /// miss some namespace qualifiers, and will be replaced by a fully
+ /// qualified one.
+ std::string RawIdentifier;
+
+ /// \brief The qualifiers of the scope in which SymbolIdentifier lookup
+ /// occurs. It is represented as a sequence of names and scope resolution
+ /// operatiors ::, ending with a scope resolution operator (e.g. a::b::).
+ /// Empty if SymbolIdentifier is not in a specific scope.
+ std::string ScopedQualifiers;
+
+ /// \brief The replacement range of RawIdentifier.
+ tooling::Range Range;
+ };
+
+ IncludeFixerContext() = default;
+ IncludeFixerContext(StringRef FilePath,
+ std::vector<QuerySymbolInfo> QuerySymbols,
+ std::vector<find_all_symbols::SymbolInfo> Symbols);
+
+ /// \brief Get symbol name.
+ llvm::StringRef getSymbolIdentifier() const {
+ return QuerySymbolInfos.front().RawIdentifier;
+ }
+
+ /// \brief Get replacement range of the symbol.
+ tooling::Range getSymbolRange() const {
+ 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<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; }
+
+ /// \brief Get information of symbols being querid.
+ const std::vector<QuerySymbolInfo> &getQuerySymbolInfos() const {
+ return QuerySymbolInfos;
+ }
+
+private:
+ friend struct llvm::yaml::MappingTraits<IncludeFixerContext>;
+
+ /// \brief The file path to the file being processed.
+ std::string FilePath;
+
+ /// \brief All instances of an unidentified symbol being queried.
+ std::vector<QuerySymbolInfo> QuerySymbolInfos;
+
+ /// \brief The symbol candidates which match SymbolIdentifier. The symbols are
+ /// sorted in a descending order based on the popularity info in SymbolInfo.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+ /// \brief The header information.
+ std::vector<HeaderInfo> HeaderInfos;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
--- /dev/null
+//===-- SymbolIndex.h - Interface for symbol-header matching ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding all `SymbolInfo`s corresponding
+/// to a symbol name from a symbol database.
+class SymbolIndex {
+public:
+ virtual ~SymbolIndex() = default;
+
+ /// Search for all `SymbolInfo`s corresponding to an identifier.
+ /// \param Identifier The unqualified identifier being searched for.
+ /// \returns A list of `SymbolInfo` candidates.
+ // FIXME: Expose the type name so we can also insert using declarations (or
+ // fix the usage)
+ virtual std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) = 0;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
--- /dev/null
+//===-- SymbolIndexManager.cpp - Managing multiple SymbolIndices-*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolIndexManager.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "include-fixer"
+
+namespace clang {
+namespace include_fixer {
+
+using find_all_symbols::SymbolInfo;
+using find_all_symbols::SymbolAndSignals;
+
+// Calculate a score based on whether we think the given header is closely
+// related to the given source file.
+static double similarityScore(llvm::StringRef FileName,
+ llvm::StringRef Header) {
+ // Compute the maximum number of common path segements between Header and
+ // a suffix of FileName.
+ // We do not do a full longest common substring computation, as Header
+ // specifies the path we would directly #include, so we assume it is rooted
+ // relatively to a subproject of the repository.
+ int MaxSegments = 1;
+ for (auto FileI = llvm::sys::path::begin(FileName),
+ FileE = llvm::sys::path::end(FileName);
+ FileI != FileE; ++FileI) {
+ int Segments = 0;
+ for (auto HeaderI = llvm::sys::path::begin(Header),
+ HeaderE = llvm::sys::path::end(Header), I = FileI;
+ HeaderI != HeaderE && *I == *HeaderI && I != FileE; ++I, ++HeaderI) {
+ ++Segments;
+ }
+ MaxSegments = std::max(Segments, MaxSegments);
+ }
+ return MaxSegments;
+}
+
+static void rank(std::vector<SymbolAndSignals> &Symbols,
+ llvm::StringRef FileName) {
+ llvm::DenseMap<llvm::StringRef, double> Score;
+ for (const auto &Symbol : Symbols) {
+ // Calculate a score from the similarity of the header the symbol is in
+ // with the current file and the popularity of the symbol.
+ double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
+ (1.0 + std::log2(1 + Symbol.Signals.Seen));
+ double &S = Score[Symbol.Symbol.getFilePath()];
+ S = std::max(S, NewScore);
+ }
+ // Sort by the gathered scores. Use file name as a tie breaker so we can
+ // deduplicate.
+ std::sort(Symbols.begin(), Symbols.end(),
+ [&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
+ auto AS = Score[A.Symbol.getFilePath()];
+ auto BS = Score[B.Symbol.getFilePath()];
+ if (AS != BS)
+ return AS > BS;
+ return A.Symbol.getFilePath() < B.Symbol.getFilePath();
+ });
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+SymbolIndexManager::search(llvm::StringRef Identifier,
+ bool IsNestedSearch,
+ llvm::StringRef FileName) const {
+ // The identifier may be fully qualified, so split it and get all the context
+ // names.
+ llvm::SmallVector<llvm::StringRef, 8> Names;
+ Identifier.split(Names, "::");
+
+ bool IsFullyQualified = false;
+ if (Identifier.startswith("::")) {
+ Names.erase(Names.begin()); // Drop first (empty) element.
+ IsFullyQualified = true;
+ }
+
+ // As long as we don't find a result keep stripping name parts from the end.
+ // This is to support nested classes which aren't recorded in the database.
+ // Eventually we will either hit a class (namespaces aren't in the database
+ // either) and can report that result.
+ bool TookPrefix = false;
+ std::vector<SymbolAndSignals> MatchedSymbols;
+ do {
+ std::vector<SymbolAndSignals> Symbols;
+ for (const auto &DB : SymbolIndices) {
+ auto Res = DB.get()->search(Names.back());
+ Symbols.insert(Symbols.end(), Res.begin(), Res.end());
+ }
+
+ DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
+ << Symbols.size() << " results...\n");
+
+ for (auto &SymAndSig : Symbols) {
+ const SymbolInfo &Symbol = SymAndSig.Symbol;
+ // Match the identifier name without qualifier.
+ bool IsMatched = true;
+ auto SymbolContext = Symbol.getContexts().begin();
+ auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name.
+ // Match the remaining context names.
+ while (IdentiferContext != Names.rend() &&
+ SymbolContext != Symbol.getContexts().end()) {
+ if (SymbolContext->second == *IdentiferContext) {
+ ++IdentiferContext;
+ ++SymbolContext;
+ } else if (SymbolContext->first ==
+ find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
+ // Skip non-scoped enum context.
+ ++SymbolContext;
+ } else {
+ IsMatched = false;
+ break;
+ }
+ }
+
+ // If the name was qualified we only want to add results if we evaluated
+ // all contexts.
+ if (IsFullyQualified)
+ IsMatched &= (SymbolContext == Symbol.getContexts().end());
+
+ // FIXME: Support full match. At this point, we only find symbols in
+ // database which end with the same contexts with the identifier.
+ if (IsMatched && IdentiferContext == Names.rend()) {
+ // If we're in a situation where we took a prefix but the thing we
+ // found couldn't possibly have a nested member ignore it.
+ if (TookPrefix &&
+ (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function ||
+ Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable ||
+ Symbol.getSymbolKind() ==
+ SymbolInfo::SymbolKind::EnumConstantDecl ||
+ Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
+ continue;
+
+ MatchedSymbols.push_back(std::move(SymAndSig));
+ }
+ }
+ Names.pop_back();
+ TookPrefix = true;
+ } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
+
+ rank(MatchedSymbols, FileName);
+ // Strip signals, they are no longer needed.
+ std::vector<SymbolInfo> Res;
+ for (auto &SymAndSig : MatchedSymbols)
+ Res.push_back(std::move(SymAndSig.Symbol));
+ return Res;
+}
+
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===-- SymbolIndexManager.h - Managing multiple SymbolIndices --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+
+#ifdef _MSC_VER
+// Disable warnings from ppltasks.h transitively included by <future>.
+#pragma warning(push)
+#pragma warning(disable:4530)
+#endif
+
+#include <future>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding the header files corresponding
+/// to an identifier in the source code from multiple symbol databases.
+class SymbolIndexManager {
+public:
+ void addSymbolIndex(std::function<std::unique_ptr<SymbolIndex>()> F) {
+#if LLVM_ENABLE_THREADS
+ auto Strategy = std::launch::async;
+#else
+ auto Strategy = std::launch::deferred;
+#endif
+ SymbolIndices.push_back(std::async(Strategy, F));
+ }
+
+ /// Search for header files to be included for an identifier.
+ /// \param Identifier The identifier being searched for. May or may not be
+ /// fully qualified.
+ /// \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<find_all_symbols::SymbolInfo>
+ search(llvm::StringRef Identifier, bool IsNestedSearch = true,
+ llvm::StringRef FileName = "") const;
+
+private:
+ std::vector<std::shared_future<std::unique_ptr<SymbolIndex>>> SymbolIndices;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif
--- /dev/null
+//===-- YamlSymbolIndex.cpp -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "YamlSymbolIndex.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include <string>
+#include <vector>
+
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromFile(llvm::StringRef FilePath) {
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer)
+ return Buffer.getError();
+
+ return std::unique_ptr<YamlSymbolIndex>(new YamlSymbolIndex(
+ find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())));
+}
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromDirectory(llvm::StringRef Directory,
+ llvm::StringRef Name) {
+ // Walk upwards from Directory, looking for files.
+ for (llvm::SmallString<128> PathStorage = Directory; !Directory.empty();
+ Directory = llvm::sys::path::parent_path(Directory)) {
+ assert(Directory.size() <= PathStorage.size());
+ PathStorage.resize(Directory.size()); // Shrink to parent.
+ llvm::sys::path::append(PathStorage, Name);
+ if (auto DB = createFromFile(PathStorage))
+ return DB;
+ }
+ return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
+}
+
+std::vector<SymbolAndSignals>
+YamlSymbolIndex::search(llvm::StringRef Identifier) {
+ std::vector<SymbolAndSignals> Results;
+ for (const auto &Symbol : Symbols) {
+ if (Symbol.Symbol.getName() == Identifier)
+ Results.push_back(Symbol);
+ }
+ return Results;
+}
+
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===-- YamlSymbolIndex.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/Support/ErrorOr.h"
+#include <map>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Yaml format database.
+class YamlSymbolIndex : public SymbolIndex {
+public:
+ /// Create a new Yaml db from a file.
+ static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+ createFromFile(llvm::StringRef FilePath);
+ /// Look for a file called \c Name in \c Directory and all parent directories.
+ static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+ createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
+
+ std::vector<find_all_symbols::SymbolAndSignals>
+ search(llvm::StringRef Identifier) override;
+
+private:
+ explicit YamlSymbolIndex(
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols)
+ : Symbols(std::move(Symbols)) {}
+
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_library(findAllSymbols
+ FindAllSymbols.cpp
+ FindAllSymbolsAction.cpp
+ FindAllMacros.cpp
+ HeaderMapCollector.cpp
+ PathConfig.cpp
+ PragmaCommentHandler.cpp
+ STLPostfixHeaderMap.cpp
+ SymbolInfo.cpp
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTooling
+ )
+
+add_subdirectory(tool)
--- /dev/null
+//===-- FindAllMacros.cpp - find all macros ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllMacros.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Token.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+llvm::Optional<SymbolInfo>
+FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
+ const MacroInfo *info) {
+ std::string FilePath =
+ getIncludePath(*SM, info->getDefinitionLoc(), Collector);
+ if (FilePath.empty())
+ return llvm::None;
+ return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
+ SymbolInfo::SymbolKind::Macro, FilePath, {});
+}
+
+void FindAllMacros::MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) {
+ if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo()))
+ ++FileSymbols[*Symbol].Seen;
+}
+
+void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
+ if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation())))
+ return;
+ if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo()))
+ ++FileSymbols[*Symbol].Used;
+}
+
+void FindAllMacros::MacroExpands(const Token &MacroNameTok,
+ const MacroDefinition &MD, SourceRange Range,
+ const MacroArgs *Args) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::EndOfMainFile() {
+ Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
+ FileSymbols);
+ FileSymbols.clear();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- FindAllMacros.h - find all macros -----------------------*- 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_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/Lex/PPCallbacks.h"
+
+namespace clang {
+class MacroInfo;
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief A preprocessor that collects all macro symbols.
+/// The contexts of a macro will be ignored since they are not available during
+/// preprocessing period.
+class FindAllMacros : public clang::PPCallbacks {
+public:
+ explicit FindAllMacros(SymbolReporter *Reporter, SourceManager *SM,
+ HeaderMapCollector *Collector = nullptr)
+ : Reporter(Reporter), SM(SM), Collector(Collector) {}
+
+ void MacroDefined(const Token &MacroNameTok,
+ const MacroDirective *MD) override;
+
+ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+ SourceRange Range, const MacroArgs *Args) override;
+
+ void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) override;
+
+ void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) override;
+
+ void EndOfMainFile() override;
+
+private:
+ llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
+ const MacroInfo *MD);
+ // Not a callback, just a common path for all usage types.
+ void MacroUsed(const Token &Name, const MacroDefinition &MD);
+
+ SymbolInfo::SignalMap FileSymbols;
+ // Reporter for SymbolInfo.
+ SymbolReporter *const Reporter;
+ SourceManager *const SM;
+ // A remapping header file collector allowing clients to include a different
+ // header.
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
--- /dev/null
+//===-- FindAllSymbols.cpp - find all symbols--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+
+AST_MATCHER(EnumConstantDecl, isInScopedEnum) {
+ if (const auto *ED = dyn_cast<EnumDecl>(Node.getDeclContext()))
+ return ED->isScoped();
+ return false;
+}
+
+AST_POLYMORPHIC_MATCHER(isFullySpecialized,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
+ CXXRecordDecl)) {
+ if (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
+ bool IsPartialSpecialization =
+ llvm::isa<VarTemplatePartialSpecializationDecl>(Node) ||
+ llvm::isa<ClassTemplatePartialSpecializationDecl>(Node);
+ return !IsPartialSpecialization;
+ }
+ return false;
+}
+
+std::vector<SymbolInfo::Context> GetContexts(const NamedDecl *ND) {
+ std::vector<SymbolInfo::Context> Contexts;
+ for (const auto *Context = ND->getDeclContext(); Context;
+ Context = Context->getParent()) {
+ if (llvm::isa<TranslationUnitDecl>(Context) ||
+ llvm::isa<LinkageSpecDecl>(Context))
+ break;
+
+ assert(llvm::isa<NamedDecl>(Context) &&
+ "Expect Context to be a NamedDecl");
+ if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {
+ if (!NSD->isInlineNamespace())
+ Contexts.emplace_back(SymbolInfo::ContextType::Namespace,
+ NSD->getName().str());
+ } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {
+ Contexts.emplace_back(SymbolInfo::ContextType::EnumDecl,
+ ED->getName().str());
+ } else {
+ const auto *RD = cast<RecordDecl>(Context);
+ Contexts.emplace_back(SymbolInfo::ContextType::Record,
+ RD->getName().str());
+ }
+ }
+ return Contexts;
+}
+
+llvm::Optional<SymbolInfo>
+CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,
+ const HeaderMapCollector *Collector) {
+ SymbolInfo::SymbolKind Type;
+ if (llvm::isa<VarDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::Variable;
+ } else if (llvm::isa<FunctionDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::Function;
+ } else if (llvm::isa<TypedefNameDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::TypedefName;
+ } else if (llvm::isa<EnumConstantDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::EnumConstantDecl;
+ } else if (llvm::isa<EnumDecl>(ND)) {
+ Type = SymbolInfo::SymbolKind::EnumDecl;
+ // Ignore anonymous enum declarations.
+ if (ND->getName().empty())
+ return llvm::None;
+ } else {
+ assert(llvm::isa<RecordDecl>(ND) &&
+ "Matched decl must be one of VarDecl, "
+ "FunctionDecl, TypedefNameDecl, EnumConstantDecl, "
+ "EnumDecl and RecordDecl!");
+ // C-style record decl can have empty name, e.g "struct { ... } var;".
+ if (ND->getName().empty())
+ return llvm::None;
+ Type = SymbolInfo::SymbolKind::Class;
+ }
+
+ SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());
+ if (!Loc.isValid()) {
+ llvm::errs() << "Declaration " << ND->getNameAsString() << "("
+ << ND->getDeclKindName()
+ << ") has invalid declaration location.";
+ return llvm::None;
+ }
+
+ std::string FilePath = getIncludePath(SM, Loc, Collector);
+ if (FilePath.empty()) return llvm::None;
+
+ return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND));
+}
+
+} // namespace
+
+void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
+ // FIXME: Handle specialization.
+ auto IsInSpecialization = hasAncestor(
+ decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+ functionDecl(isExplicitTemplateSpecialization()))));
+
+ // Matchers for both C and C++.
+ // We only match symbols from header files, i.e. not from main files (see
+ // function's comment for detailed explanation).
+ auto CommonFilter =
+ allOf(unless(isImplicit()), unless(isExpansionInMainFile()));
+
+ auto HasNSOrTUCtxMatcher =
+ hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
+
+ // We need seperate rules for C record types and C++ record types since some
+ // template related matchers are inapplicable on C record declarations.
+ //
+ // Matchers specific to C++ code.
+ // All declarations should be in namespace or translation unit.
+ auto CCMatcher =
+ allOf(HasNSOrTUCtxMatcher, unless(IsInSpecialization),
+ unless(ast_matchers::isTemplateInstantiation()),
+ unless(isInstantiated()), unless(isFullySpecialized()));
+
+ // Matchers specific to code in extern "C" {...}.
+ auto ExternCMatcher = hasDeclContext(linkageSpecDecl());
+
+ // Matchers for variable declarations.
+ //
+ // In most cases, `ParmVarDecl` is filtered out by hasDeclContext(...)
+ // matcher since the declaration context is usually `MethodDecl`. However,
+ // this assumption does not hold for parameters of a function pointer
+ // parameter.
+ // For example, consider a function declaration:
+ // void Func(void (*)(float), int);
+ // The float parameter of the function pointer has an empty name, and its
+ // declaration context is an anonymous namespace; therefore, it won't be
+ // filtered out by our matchers above.
+ auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
+ unless(parmVarDecl()));
+
+ // Matchers for C-style record declarations in extern "C" {...}.
+ auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
+ // Matchers for C++ record declarations.
+ auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
+
+ // Matchers for function declarations.
+ // We want to exclude friend declaration, but the `DeclContext` of a friend
+ // function declaration is not the class in which it is declared, so we need
+ // to explicitly check if the parent is a `friendDecl`.
+ auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
+ anyOf(ExternCMatcher, CCMatcher));
+
+ // Matcher for typedef and type alias declarations.
+ //
+ // typedef and type alias can come from C-style headers and C++ headers.
+ // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
+ // or `LinkageSpecDecl`.
+ // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
+ // or `NamespaceDecl`.
+ // With the following context matcher, we can match `typedefNameDecl` from
+ // both C-style headers and C++ headers (except for those in classes).
+ // "cc_matchers" are not included since template-related matchers are not
+ // applicable on `TypedefNameDecl`.
+ auto Typedefs =
+ typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
+ hasDeclContext(linkageSpecDecl())));
+
+ // Matchers for enum declarations.
+ auto Enums = enumDecl(CommonFilter, isDefinition(),
+ anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
+
+ // Matchers for enum constant declarations.
+ // We only match the enum constants in non-scoped enum declarations which are
+ // inside toplevel translation unit or a namespace.
+ auto EnumConstants = enumConstantDecl(
+ CommonFilter, unless(isInScopedEnum()),
+ anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
+
+ // Most of the time we care about all matchable decls, or all types.
+ auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums));
+ auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
+ EnumConstants, Functions));
+
+ // We want eligible decls bound to "decl"...
+ MatchFinder->addMatcher(Decls.bind("decl"), this);
+
+ // ... and all uses of them bound to "use". These have many cases:
+ // Uses of values/functions: these generate a declRefExpr.
+ MatchFinder->addMatcher(
+ declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
+ // Uses of function templates:
+ MatchFinder->addMatcher(
+ declRefExpr(isExpansionInMainFile(),
+ to(functionDecl(hasParent(
+ functionTemplateDecl(has(Functions.bind("use"))))))),
+ this);
+
+ // Uses of most types: just look at what the typeLoc refers to.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(qualType(hasDeclaration(Types.bind("use"))))),
+ this);
+ // Uses of typedefs: these are often transparent to hasDeclaration, so we need
+ // to handle them explicitly.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
+ this);
+ // Uses of class templates:
+ // The typeLoc names the templateSpecializationType. Its declaration is the
+ // ClassTemplateDecl, which contains the CXXRecordDecl we want.
+ MatchFinder->addMatcher(
+ typeLoc(isExpansionInMainFile(),
+ loc(templateSpecializationType(hasDeclaration(
+ classTemplateDecl(has(CXXRecords.bind("use"))))))),
+ this);
+}
+
+void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
+ // Ignore Results in failing TUs.
+ if (Result.Context->getDiagnostics().hasErrorOccurred()) {
+ return;
+ }
+
+ SymbolInfo::Signals Signals;
+ const NamedDecl *ND;
+ if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))
+ Signals.Used = 1;
+ else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))
+ Signals.Seen = 1;
+ else
+ assert(false && "Must match a NamedDecl!");
+
+ const SourceManager *SM = Result.SourceManager;
+ if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
+ Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
+ FileSymbols[*Symbol] += Signals;
+ }
+}
+
+void FindAllSymbols::onEndOfTranslationUnit() {
+ if (Filename != "") {
+ Reporter->reportSymbols(Filename, FileSymbols);
+ FileSymbols.clear();
+ Filename = "";
+ }
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- FindAllSymbols.h - find all symbols----------------------*- 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_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief FindAllSymbols collects all classes, free standing functions and
+/// global variables with some extra information such as the path of the header
+/// file, the namespaces they are contained in, the type of variables and the
+/// parameter types of functions.
+///
+/// NOTE:
+/// - Symbols declared in main files are not collected since they can not be
+/// included.
+/// - Member functions are not collected because accessing them must go
+/// through the class. #include fixer only needs the class name to find
+/// headers.
+///
+class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback {
+public:
+ explicit FindAllSymbols(SymbolReporter *Reporter,
+ HeaderMapCollector *Collector = nullptr)
+ : Reporter(Reporter), Collector(Collector) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
+
+ void run(const ast_matchers::MatchFinder::MatchResult &result) override;
+
+protected:
+ void onEndOfTranslationUnit() override;
+
+private:
+ // Current source file being processed, filled by first symbol found.
+ std::string Filename;
+ // Findings for the current source file, flushed on onEndOfTranslationUnit.
+ SymbolInfo::SignalMap FileSymbols;
+ // Reporter for SymbolInfo.
+ SymbolReporter *const Reporter;
+ // A remapping header file collector allowing clients include a different
+ // header.
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
--- /dev/null
+//===-- FindAllSymbolsAction.cpp - find all symbols action --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "FindAllMacros.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+FindAllSymbolsAction::FindAllSymbolsAction(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap)
+ : Reporter(Reporter), Collector(RegexHeaderMap), Handler(&Collector),
+ Matcher(Reporter, &Collector) {
+ Matcher.registerMatchers(&MatchFinder);
+}
+
+std::unique_ptr<ASTConsumer>
+FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef InFile) {
+ Compiler.getPreprocessor().addCommentHandler(&Handler);
+ Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllMacros>(
+ Reporter, &Compiler.getSourceManager(), &Collector));
+ return MatchFinder.newASTConsumer();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- FindAllSymbolsAction.h - find all symbols action --------*- 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_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PragmaCommentHandler.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace clang {
+namespace find_all_symbols {
+
+class FindAllSymbolsAction : public clang::ASTFrontendAction {
+public:
+ explicit FindAllSymbolsAction(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr);
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ StringRef InFile) override;
+
+private:
+ SymbolReporter *const Reporter;
+ clang::ast_matchers::MatchFinder MatchFinder;
+ HeaderMapCollector Collector;
+ PragmaCommentHandler Handler;
+ FindAllSymbols Matcher;
+};
+
+class FindAllSymbolsActionFactory : public tooling::FrontendActionFactory {
+public:
+ FindAllSymbolsActionFactory(
+ SymbolReporter *Reporter,
+ const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr)
+ : Reporter(Reporter), RegexHeaderMap(RegexHeaderMap) {}
+
+ clang::FrontendAction *create() override {
+ return new FindAllSymbolsAction(Reporter, RegexHeaderMap);
+ }
+
+private:
+ SymbolReporter *const Reporter;
+ const HeaderMapCollector::RegexHeaderMap *const RegexHeaderMap;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
--- /dev/null
+//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderMapCollector.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+llvm::StringRef
+HeaderMapCollector::getMappedHeader(llvm::StringRef Header) const {
+ auto Iter = HeaderMappingTable.find(Header);
+ if (Iter != HeaderMappingTable.end())
+ return Iter->second;
+ // If there is no complete header name mapping for this header, check the
+ // regex header mapping.
+ if (RegexHeaderMappingTable) {
+ for (const auto &Entry : *RegexHeaderMappingTable) {
+ if (llvm::Regex(Entry.first).match(Header))
+ return Entry.second;
+ }
+ }
+ return Header;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- HeaderMapCoolector.h - find all symbols------------------*- 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_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief HeaderMappCollector collects all remapping header files. This maps
+/// complete header names or header name regex patterns to header names.
+class HeaderMapCollector {
+public:
+ typedef llvm::StringMap<std::string> HeaderMap;
+ typedef std::vector<std::pair<const char *, const char *>> RegexHeaderMap;
+
+ HeaderMapCollector() : RegexHeaderMappingTable(nullptr) {}
+
+ explicit HeaderMapCollector(const RegexHeaderMap *RegexHeaderMappingTable)
+ : RegexHeaderMappingTable(RegexHeaderMappingTable) {}
+
+ void addHeaderMapping(llvm::StringRef OrignalHeaderPath,
+ llvm::StringRef MappingHeaderPath) {
+ HeaderMappingTable[OrignalHeaderPath] = MappingHeaderPath;
+ };
+
+ /// Check if there is a mapping from \p Header or a regex pattern that matches
+ /// it to another header name.
+ /// \param Header A header name.
+ /// \return \p Header itself if there is no mapping for it; otherwise, return
+ /// a mapped header name.
+ llvm::StringRef getMappedHeader(llvm::StringRef Header) const;
+
+private:
+ /// A string-to-string map saving the mapping relationship.
+ HeaderMap HeaderMappingTable;
+
+ // A map from header patterns to header names.
+ // This is a reference to a hard-coded map.
+ const RegexHeaderMap *const RegexHeaderMappingTable;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
--- /dev/null
+//===-- PathConfig.cpp - Process paths of symbols ---------------*- C++ -*-===//
+//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PathConfig.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+ const HeaderMapCollector *Collector) {
+ llvm::StringRef FilePath;
+ // Walk up the include stack to skip .inc files.
+ while (true) {
+ if (!Loc.isValid() || SM.isInMainFile(Loc))
+ return "";
+ FilePath = SM.getFilename(Loc);
+ if (FilePath.empty())
+ return "";
+ if (!FilePath.endswith(".inc"))
+ break;
+ FileID ID = SM.getFileID(Loc);
+ Loc = SM.getIncludeLoc(ID);
+ }
+
+ if (Collector)
+ FilePath = Collector->getMappedHeader(FilePath);
+ SmallString<256> CleanedFilePath = FilePath;
+ llvm::sys::path::remove_dots(CleanedFilePath, /*remove_dot_dot=*/false);
+
+ return CleanedFilePath.str();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- PathConfig.h - Process paths of symbols -----------------*- 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_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+
+#include "HeaderMapCollector.h"
+#include "clang/Basic/SourceManager.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief This calculates the include path for \p Loc.
+///
+/// \param SM SourceManager.
+/// \param Loc A SourceLocation.
+/// \param Collector An optional header mapping collector.
+///
+/// \return The file path (or mapped file path if Collector is provided) of the
+/// header that includes \p Loc. If \p Loc comes from .inc header file, \p Loc
+/// is set to the location from which the .inc header file is included. If \p
+/// Loc is invalid or comes from a main file, this returns an empty string.
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+ const HeaderMapCollector *Collector = nullptr);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
--- /dev/null
+//===-- PragmaCommentHandler.cpp - find all symbols -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PragmaCommentHandler.h"
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+const char IWYUPragma[] = "// IWYU pragma: private, include ";
+} // namespace
+
+bool PragmaCommentHandler::HandleComment(Preprocessor &PP, SourceRange Range) {
+ StringRef Text =
+ Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+ PP.getSourceManager(), PP.getLangOpts());
+ size_t Pos = Text.find(IWYUPragma);
+ if (Pos == StringRef::npos)
+ return false;
+ StringRef RemappingFilePath = Text.substr(Pos + std::strlen(IWYUPragma));
+ Collector->addHeaderMapping(
+ PP.getSourceManager().getFilename(Range.getBegin()),
+ RemappingFilePath.trim("\"<>"));
+ return false;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- PragmaCommentHandler.h - find all symbols----------------*- 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_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Preprocessor.h"
+#include <map>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief PragmaCommentHandler parses pragma comment on include files to
+/// determine when we should include a different header from the header that
+/// directly defines a symbol.
+///
+/// Currently it only supports IWYU private pragma:
+/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private
+class PragmaCommentHandler : public clang::CommentHandler {
+public:
+ PragmaCommentHandler(HeaderMapCollector *Collector) : Collector(Collector) {}
+
+ bool HandleComment(Preprocessor &PP, SourceRange Range) override;
+
+private:
+ HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
--- /dev/null
+//===-- STLPostfixHeaderMap.h - hardcoded STL header map --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "STLPostfixHeaderMap.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() {
+ static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = {
+ {"include/__stddef_max_align_t.h$", "<cstddef>"},
+ {"include/__wmmintrin_aes.h$", "<wmmintrin.h>"},
+ {"include/__wmmintrin_pclmul.h$", "<wmmintrin.h>"},
+ {"include/adxintrin.h$", "<immintrin.h>"},
+ {"include/ammintrin.h$", "<ammintrin.h>"},
+ {"include/avx2intrin.h$", "<immintrin.h>"},
+ {"include/avx512bwintrin.h$", "<immintrin.h>"},
+ {"include/avx512cdintrin.h$", "<immintrin.h>"},
+ {"include/avx512dqintrin.h$", "<immintrin.h>"},
+ {"include/avx512erintrin.h$", "<immintrin.h>"},
+ {"include/avx512fintrin.h$", "<immintrin.h>"},
+ {"include/avx512ifmaintrin.h$", "<immintrin.h>"},
+ {"include/avx512ifmavlintrin.h$", "<immintrin.h>"},
+ {"include/avx512pfintrin.h$", "<immintrin.h>"},
+ {"include/avx512vbmiintrin.h$", "<immintrin.h>"},
+ {"include/avx512vbmivlintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlbwintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlcdintrin.h$", "<immintrin.h>"},
+ {"include/avx512vldqintrin.h$", "<immintrin.h>"},
+ {"include/avx512vlintrin.h$", "<immintrin.h>"},
+ {"include/avxintrin.h$", "<immintrin.h>"},
+ {"include/bmi2intrin.h$", "<x86intrin.h>"},
+ {"include/bmiintrin.h$", "<x86intrin.h>"},
+ {"include/emmintrin.h$", "<emmintrin.h>"},
+ {"include/f16cintrin.h$", "<emmintrin.h>"},
+ {"include/float.h$", "<cfloat>"},
+ {"include/fma4intrin.h$", "<x86intrin.h>"},
+ {"include/fmaintrin.h$", "<immintrin.h>"},
+ {"include/fxsrintrin.h$", "<immintrin.h>"},
+ {"include/ia32intrin.h$", "<x86intrin.h>"},
+ {"include/immintrin.h$", "<immintrin.h>"},
+ {"include/inttypes.h$", "<cinttypes>"},
+ {"include/limits.h$", "<climits>"},
+ {"include/lzcntintrin.h$", "<x86intrin.h>"},
+ {"include/mm3dnow.h$", "<mm3dnow.h>"},
+ {"include/mm_malloc.h$", "<mm_malloc.h>"},
+ {"include/mmintrin.h$", "<mmintrin>"},
+ {"include/mwaitxintrin.h$", "<x86intrin.h>"},
+ {"include/pkuintrin.h$", "<immintrin.h>"},
+ {"include/pmmintrin.h$", "<pmmintrin.h>"},
+ {"include/popcntintrin.h$", "<popcntintrin.h>"},
+ {"include/prfchwintrin.h$", "<x86intrin.h>"},
+ {"include/rdseedintrin.h$", "<x86intrin.h>"},
+ {"include/rtmintrin.h$", "<immintrin.h>"},
+ {"include/shaintrin.h$", "<immintrin.h>"},
+ {"include/smmintrin.h$", "<smmintrin.h>"},
+ {"include/stdalign.h$", "<cstdalign>"},
+ {"include/stdarg.h$", "<cstdarg>"},
+ {"include/stdbool.h$", "<cstdbool>"},
+ {"include/stddef.h$", "<cstddef>"},
+ {"include/stdint.h$", "<cstdint>"},
+ {"include/tbmintrin.h$", "<x86intrin.h>"},
+ {"include/tmmintrin.h$", "<tmmintrin.h>"},
+ {"include/wmmintrin.h$", "<wmmintrin.h>"},
+ {"include/x86intrin.h$", "<x86intrin.h>"},
+ {"include/xmmintrin.h$", "<xmmintrin.h>"},
+ {"include/xopintrin.h$", "<x86intrin.h>"},
+ {"include/xsavecintrin.h$", "<immintrin.h>"},
+ {"include/xsaveintrin.h$", "<immintrin.h>"},
+ {"include/xsaveoptintrin.h$", "<immintrin.h>"},
+ {"include/xsavesintrin.h$", "<immintrin.h>"},
+ {"include/xtestintrin.h$", "<immintrin.h>"},
+ {"include/_G_config.h$", "<cstdio>"},
+ {"include/assert.h$", "<cassert>"},
+ {"algorithm$", "<algorithm>"},
+ {"array$", "<array>"},
+ {"atomic$", "<atomic>"},
+ {"backward/auto_ptr.h$", "<memory>"},
+ {"backward/binders.h$", "<string>"},
+ {"bits/algorithmfwd.h$", "<algorithm>"},
+ {"bits/alloc_traits.h$", "<unordered_set>"},
+ {"bits/allocator.h$", "<string>"},
+ {"bits/atomic_base.h$", "<atomic>"},
+ {"bits/atomic_lockfree_defines.h$", "<exception>"},
+ {"bits/basic_ios.h$", "<ios>"},
+ {"bits/basic_ios.tcc$", "<ios>"},
+ {"bits/basic_string.h$", "<string>"},
+ {"bits/basic_string.tcc$", "<string>"},
+ {"bits/char_traits.h$", "<string>"},
+ {"bits/codecvt.h$", "<locale>"},
+ {"bits/concept_check.h$", "<numeric>"},
+ {"bits/cpp_type_traits.h$", "<cmath>"},
+ {"bits/cxxabi_forced.h$", "<cxxabi.h>"},
+ {"bits/deque.tcc$", "<deque>"},
+ {"bits/exception_defines.h$", "<exception>"},
+ {"bits/exception_ptr.h$", "<exception>"},
+ {"bits/forward_list.h$", "<forward_list>"},
+ {"bits/forward_list.tcc$", "<forward_list>"},
+ {"bits/fstream.tcc$", "<fstream>"},
+ {"bits/functexcept.h$", "<list>"},
+ {"bits/functional_hash.h$", "<string>"},
+ {"bits/gslice.h$", "<valarray>"},
+ {"bits/gslice_array.h$", "<valarray>"},
+ {"bits/hash_bytes.h$", "<typeinfo>"},
+ {"bits/hashtable.h$", "<unordered_set>"},
+ {"bits/hashtable_policy.h$", "<unordered_set>"},
+ {"bits/indirect_array.h$", "<valarray>"},
+ {"bits/ios_base.h$", "<ios>"},
+ {"bits/istream.tcc$", "<istream>"},
+ {"bits/list.tcc$", "<list>"},
+ {"bits/locale_classes.h$", "<locale>"},
+ {"bits/locale_classes.tcc$", "<locale>"},
+ {"bits/locale_facets.h$", "<locale>"},
+ {"bits/locale_facets.tcc$", "<locale>"},
+ {"bits/locale_facets_nonio.h$", "<locale>"},
+ {"bits/locale_facets_nonio.tcc$", "<locale>"},
+ {"bits/localefwd.h$", "<locale>"},
+ {"bits/mask_array.h$", "<valarray>"},
+ {"bits/memoryfwd.h$", "<memory>"},
+ {"bits/move.h$", "<utility>"},
+ {"bits/nested_exception.h$", "<exception>"},
+ {"bits/ostream.tcc$", "<ostream>"},
+ {"bits/ostream_insert.h$", "<ostream>"},
+ {"bits/postypes.h$", "<iosfwd>"},
+ {"bits/ptr_traits.h$", "<memory>"},
+ {"bits/random.h$", "<random>"},
+ {"bits/random.tcc$", "<random>"},
+ {"bits/range_access.h$", "<iterator>"},
+ {"bits/regex.h$", "<regex>"},
+ {"bits/regex_compiler.h$", "<regex>"},
+ {"bits/regex_constants.h$", "<regex>"},
+ {"bits/regex_cursor.h$", "<regex>"},
+ {"bits/regex_error.h$", "<regex>"},
+ {"bits/regex_grep_matcher.h$", "<regex>"},
+ {"bits/regex_grep_matcher.tcc$", "<regex>"},
+ {"bits/regex_nfa.h$", "<regex>"},
+ {"bits/shared_ptr.h$", "<memory>"},
+ {"bits/shared_ptr_base.h$", "<memory>"},
+ {"bits/slice_array.h$", "<valarray>"},
+ {"bits/sstream.tcc$", "<sstream>"},
+ {"bits/stl_algo.h$", "<algorithm>"},
+ {"bits/stl_algobase.h$", "<list>"},
+ {"bits/stl_bvector.h$", "<vector>"},
+ {"bits/stl_construct.h$", "<deque>"},
+ {"bits/stl_deque.h$", "<deque>"},
+ {"bits/stl_function.h$", "<string>"},
+ {"bits/stl_heap.h$", "<queue>"},
+ {"bits/stl_iterator.h$", "<iterator>"},
+ {"bits/stl_iterator_base_funcs.h$", "<iterator>"},
+ {"bits/stl_iterator_base_types.h$", "<numeric>"},
+ {"bits/stl_list.h$", "<list>"},
+ {"bits/stl_map.h$", "<map>"},
+ {"bits/stl_multimap.h$", "<map>"},
+ {"bits/stl_multiset.h$", "<set>"},
+ {"bits/stl_numeric.h$", "<numeric>"},
+ {"bits/stl_pair.h$", "<utility>"},
+ {"bits/stl_queue.h$", "<queue>"},
+ {"bits/stl_raw_storage_iter.h$", "<memory>"},
+ {"bits/stl_relops.h$", "<utility>"},
+ {"bits/stl_set.h$", "<set>"},
+ {"bits/stl_stack.h$", "<stack>"},
+ {"bits/stl_tempbuf.h$", "<memory>"},
+ {"bits/stl_tree.h$", "<map>"},
+ {"bits/stl_uninitialized.h$", "<deque>"},
+ {"bits/stl_vector.h$", "<vector>"},
+ {"bits/stream_iterator.h$", "<iterator>"},
+ {"bits/streambuf.tcc$", "<streambuf>"},
+ {"bits/streambuf_iterator.h$", "<iterator>"},
+ {"bits/stringfwd.h$", "<string>"},
+ {"bits/unique_ptr.h$", "<memory>"},
+ {"bits/unordered_map.h$", "<unordered_map>"},
+ {"bits/unordered_set.h$", "<unordered_set>"},
+ {"bits/uses_allocator.h$", "<tuple>"},
+ {"bits/valarray_after.h$", "<valarray>"},
+ {"bits/valarray_array.h$", "<valarray>"},
+ {"bits/valarray_array.tcc$", "<valarray>"},
+ {"bits/valarray_before.h$", "<valarray>"},
+ {"bits/vector.tcc$", "<vector>"},
+ {"bitset$", "<bitset>"},
+ {"ccomplex$", "<ccomplex>"},
+ {"cctype$", "<cctype>"},
+ {"cerrno$", "<cerrno>"},
+ {"cfenv$", "<cfenv>"},
+ {"cfloat$", "<cfloat>"},
+ {"chrono$", "<chrono>"},
+ {"cinttypes$", "<cinttypes>"},
+ {"climits$", "<climits>"},
+ {"clocale$", "<clocale>"},
+ {"cmath$", "<cmath>"},
+ {"complex$", "<complex>"},
+ {"complex.h$", "<complex.h>"},
+ {"condition_variable$", "<condition_variable>"},
+ {"csetjmp$", "<csetjmp>"},
+ {"csignal$", "<csignal>"},
+ {"cstdalign$", "<cstdalign>"},
+ {"cstdarg$", "<cstdarg>"},
+ {"cstdbool$", "<cstdbool>"},
+ {"cstdint$", "<cstdint>"},
+ {"cstdio$", "<cstdio>"},
+ {"cstdlib$", "<cstdlib>"},
+ {"cstring$", "<cstring>"},
+ {"ctgmath$", "<ctgmath>"},
+ {"ctime$", "<ctime>"},
+ {"cwchar$", "<cwchar>"},
+ {"cwctype$", "<cwctype>"},
+ {"cxxabi.h$", "<cxxabi.h>"},
+ {"debug/debug.h$", "<numeric>"},
+ {"deque$", "<deque>"},
+ {"exception$", "<exception>"},
+ {"ext/alloc_traits.h$", "<deque>"},
+ {"ext/atomicity.h$", "<memory>"},
+ {"ext/concurrence.h$", "<memory>"},
+ {"ext/new_allocator.h$", "<string>"},
+ {"ext/numeric_traits.h$", "<list>"},
+ {"ext/string_conversions.h$", "<string>"},
+ {"ext/type_traits.h$", "<cmath>"},
+ {"fenv.h$", "<fenv.h>"},
+ {"forward_list$", "<forward_list>"},
+ {"fstream$", "<fstream>"},
+ {"functional$", "<functional>"},
+ {"future$", "<future>"},
+ {"initializer_list$", "<initializer_list>"},
+ {"iomanip$", "<iomanip>"},
+ {"ios$", "<ios>"},
+ {"iosfwd$", "<iosfwd>"},
+ {"iostream$", "<iostream>"},
+ {"istream$", "<istream>"},
+ {"iterator$", "<iterator>"},
+ {"limits$", "<limits>"},
+ {"list$", "<list>"},
+ {"locale$", "<locale>"},
+ {"map$", "<map>"},
+ {"memory$", "<memory>"},
+ {"mutex$", "<mutex>"},
+ {"new$", "<new>"},
+ {"numeric$", "<numeric>"},
+ {"ostream$", "<ostream>"},
+ {"queue$", "<queue>"},
+ {"random$", "<random>"},
+ {"ratio$", "<ratio>"},
+ {"regex$", "<regex>"},
+ {"scoped_allocator$", "<scoped_allocator>"},
+ {"set$", "<set>"},
+ {"sstream$", "<sstream>"},
+ {"stack$", "<stack>"},
+ {"stdexcept$", "<stdexcept>"},
+ {"streambuf$", "<streambuf>"},
+ {"string$", "<string>"},
+ {"system_error$", "<system_error>"},
+ {"tgmath.h$", "<tgmath.h>"},
+ {"thread$", "<thread>"},
+ {"tuple$", "<tuple>"},
+ {"type_traits$", "<type_traits>"},
+ {"typeindex$", "<typeindex>"},
+ {"typeinfo$", "<typeinfo>"},
+ {"unordered_map$", "<unordered_map>"},
+ {"unordered_set$", "<unordered_set>"},
+ {"utility$", "<utility>"},
+ {"valarray$", "<valarray>"},
+ {"vector$", "<vector>"},
+ {"include/complex.h$", "<complex.h>"},
+ {"include/ctype.h$", "<cctype>"},
+ {"include/errno.h$", "<cerrno>"},
+ {"include/fenv.h$", "<fenv.h>"},
+ {"include/inttypes.h$", "<cinttypes>"},
+ {"include/libio.h$", "<cstdio>"},
+ {"include/limits.h$", "<climits>"},
+ {"include/locale.h$", "<clocale>"},
+ {"include/math.h$", "<cmath>"},
+ {"include/setjmp.h$", "<csetjmp>"},
+ {"include/signal.h$", "<csignal>"},
+ {"include/stdint.h$", "<cstdint>"},
+ {"include/stdio.h$", "<cstdio>"},
+ {"include/stdlib.h$", "<cstdlib>"},
+ {"include/string.h$", "<cstring>"},
+ {"include/time.h$", "<ctime>"},
+ {"include/wchar.h$", "<cwchar>"},
+ {"include/wctype.h$", "<cwctype>"},
+ {"bits/cmathcalls.h$", "<complex.h>"},
+ {"bits/errno.h$", "<cerrno>"},
+ {"bits/fenv.h$", "<fenv.h>"},
+ {"bits/huge_val.h$", "<cmath>"},
+ {"bits/huge_valf.h$", "<cmath>"},
+ {"bits/huge_vall.h$", "<cmath>"},
+ {"bits/inf.h$", "<cmath>"},
+ {"bits/local_lim.h$", "<climits>"},
+ {"bits/locale.h$", "<clocale>"},
+ {"bits/mathcalls.h$", "<math.h>"},
+ {"bits/mathdef.h$", "<cmath>"},
+ {"bits/nan.h$", "<cmath>"},
+ {"bits/posix1_lim.h$", "<climits>"},
+ {"bits/posix2_lim.h$", "<climits>"},
+ {"bits/setjmp.h$", "<csetjmp>"},
+ {"bits/sigaction.h$", "<csignal>"},
+ {"bits/sigcontext.h$", "<csignal>"},
+ {"bits/siginfo.h$", "<csignal>"},
+ {"bits/signum.h$", "<csignal>"},
+ {"bits/sigset.h$", "<csignal>"},
+ {"bits/sigstack.h$", "<csignal>"},
+ {"bits/stdio_lim.h$", "<cstdio>"},
+ {"bits/sys_errlist.h$", "<cstdio>"},
+ {"bits/time.h$", "<ctime>"},
+ {"bits/timex.h$", "<ctime>"},
+ {"bits/typesizes.h$", "<cstdio>"},
+ {"bits/wchar.h$", "<cwchar>"},
+ {"bits/wordsize.h$", "<csetjmp>"},
+ {"bits/xopen_lim.h$", "<climits>"},
+ {"include/xlocale.h$", "<cstring>"},
+ {"bits/atomic_word.h$", "<memory>"},
+ {"bits/basic_file.h$", "<fstream>"},
+ {"bits/c++allocator.h$", "<string>"},
+ {"bits/c++config.h$", "<iosfwd>"},
+ {"bits/c++io.h$", "<ios>"},
+ {"bits/c++locale.h$", "<locale>"},
+ {"bits/cpu_defines.h$", "<iosfwd>"},
+ {"bits/ctype_base.h$", "<locale>"},
+ {"bits/cxxabi_tweaks.h$", "<cxxabi.h>"},
+ {"bits/error_constants.h$", "<system_error>"},
+ {"bits/gthr-default.h$", "<memory>"},
+ {"bits/gthr.h$", "<memory>"},
+ {"bits/opt_random.h$", "<random>"},
+ {"bits/os_defines.h$", "<iosfwd>"},
+ // GNU C headers
+ {"include/aio.h$", "<aio.h>"},
+ {"include/aliases.h$", "<aliases.h>"},
+ {"include/alloca.h$", "<alloca.h>"},
+ {"include/ar.h$", "<ar.h>"},
+ {"include/argp.h$", "<argp.h>"},
+ {"include/argz.h$", "<argz.h>"},
+ {"include/arpa/nameser.h$", "<resolv.h>"},
+ {"include/arpa/nameser_compat.h$", "<resolv.h>"},
+ {"include/byteswap.h$", "<byteswap.h>"},
+ {"include/cpio.h$", "<cpio.h>"},
+ {"include/crypt.h$", "<crypt.h>"},
+ {"include/dirent.h$", "<dirent.h>"},
+ {"include/dlfcn.h$", "<dlfcn.h>"},
+ {"include/elf.h$", "<elf.h>"},
+ {"include/endian.h$", "<endian.h>"},
+ {"include/envz.h$", "<envz.h>"},
+ {"include/err.h$", "<err.h>"},
+ {"include/error.h$", "<error.h>"},
+ {"include/execinfo.h$", "<execinfo.h>"},
+ {"include/fcntl.h$", "<fcntl.h>"},
+ {"include/features.h$", "<features.h>"},
+ {"include/fenv.h$", "<fenv.h>"},
+ {"include/fmtmsg.h$", "<fmtmsg.h>"},
+ {"include/fnmatch.h$", "<fnmatch.h>"},
+ {"include/fstab.h$", "<fstab.h>"},
+ {"include/fts.h$", "<fts.h>"},
+ {"include/ftw.h$", "<ftw.h>"},
+ {"include/gconv.h$", "<gconv.h>"},
+ {"include/getopt.h$", "<getopt.h>"},
+ {"include/glob.h$", "<glob.h>"},
+ {"include/grp.h$", "<grp.h>"},
+ {"include/gshadow.h$", "<gshadow.h>"},
+ {"include/iconv.h$", "<iconv.h>"},
+ {"include/ifaddrs.h$", "<ifaddrs.h>"},
+ {"include/kdb.h$", "<kdb.h>"},
+ {"include/langinfo.h$", "<langinfo.h>"},
+ {"include/libgen.h$", "<libgen.h>"},
+ {"include/libintl.h$", "<libintl.h>"},
+ {"include/link.h$", "<link.h>"},
+ {"include/malloc.h$", "<malloc.h>"},
+ {"include/mcheck.h$", "<mcheck.h>"},
+ {"include/memory.h$", "<memory.h>"},
+ {"include/mntent.h$", "<mntent.h>"},
+ {"include/monetary.h$", "<monetary.h>"},
+ {"include/mqueue.h$", "<mqueue.h>"},
+ {"include/netdb.h$", "<netdb.h>"},
+ {"include/netinet/in.h$", "<netinet/in.h>"},
+ {"include/nl_types.h$", "<nl_types.h>"},
+ {"include/nss.h$", "<nss.h>"},
+ {"include/obstack.h$", "<obstack.h>"},
+ {"include/panel.h$", "<panel.h>"},
+ {"include/paths.h$", "<paths.h>"},
+ {"include/printf.h$", "<printf.h>"},
+ {"include/profile.h$", "<profile.h>"},
+ {"include/pthread.h$", "<pthread.h>"},
+ {"include/pty.h$", "<pty.h>"},
+ {"include/pwd.h$", "<pwd.h>"},
+ {"include/re_comp.h$", "<re_comp.h>"},
+ {"include/regex.h$", "<regex.h>"},
+ {"include/regexp.h$", "<regexp.h>"},
+ {"include/resolv.h$", "<resolv.h>"},
+ {"include/rpc/netdb.h$", "<netdb.h>"},
+ {"include/sched.h$", "<sched.h>"},
+ {"include/search.h$", "<search.h>"},
+ {"include/semaphore.h$", "<semaphore.h>"},
+ {"include/sgtty.h$", "<sgtty.h>"},
+ {"include/shadow.h$", "<shadow.h>"},
+ {"include/spawn.h$", "<spawn.h>"},
+ {"include/stab.h$", "<stab.h>"},
+ {"include/stdc-predef.h$", "<stdc-predef.h>"},
+ {"include/stdio_ext.h$", "<stdio_ext.h>"},
+ {"include/strings.h$", "<strings.h>"},
+ {"include/stropts.h$", "<stropts.h>"},
+ {"include/sudo_plugin.h$", "<sudo_plugin.h>"},
+ {"include/sysexits.h$", "<sysexits.h>"},
+ {"include/tar.h$", "<tar.h>"},
+ {"include/tcpd.h$", "<tcpd.h>"},
+ {"include/term.h$", "<term.h>"},
+ {"include/term_entry.h$", "<term_entry.h>"},
+ {"include/termcap.h$", "<termcap.h>"},
+ {"include/termios.h$", "<termios.h>"},
+ {"include/thread_db.h$", "<thread_db.h>"},
+ {"include/tic.h$", "<tic.h>"},
+ {"include/ttyent.h$", "<ttyent.h>"},
+ {"include/uchar.h$", "<uchar.h>"},
+ {"include/ucontext.h$", "<ucontext.h>"},
+ {"include/ulimit.h$", "<ulimit.h>"},
+ {"include/unctrl.h$", "<unctrl.h>"},
+ {"include/unistd.h$", "<unistd.h>"},
+ {"include/utime.h$", "<utime.h>"},
+ {"include/utmp.h$", "<utmp.h>"},
+ {"include/utmpx.h$", "<utmpx.h>"},
+ {"include/values.h$", "<values.h>"},
+ {"include/wordexp.h$", "<wordexp.h>"},
+ {"fpu_control.h$", "<fpu_control.h>"},
+ {"ieee754.h$", "<ieee754.h>"},
+ {"include/xlocale.h$", "<xlocale.h>"},
+ {"gnu/lib-names.h$", "<gnu/lib-names.h>"},
+ {"gnu/libc-version.h$", "<gnu/libc-version.h>"},
+ {"gnu/option-groups.h$", "<gnu/option-groups.h>"},
+ {"gnu/stubs-32.h$", "<gnu/stubs-32.h>"},
+ {"gnu/stubs-64.h$", "<gnu/stubs-64.h>"},
+ {"gnu/stubs-x32.h$", "<gnu/stubs-x32.h>"},
+ {"include/rpc/auth_des.h$", "<rpc/auth_des.h>"},
+ {"include/rpc/rpc_msg.h$", "<rpc/rpc_msg.h>"},
+ {"include/rpc/pmap_clnt.h$", "<rpc/pmap_clnt.h>"},
+ {"include/rpc/rpc.h$", "<rpc/rpc.h>"},
+ {"include/rpc/types.h$", "<rpc/types.h>"},
+ {"include/rpc/auth_unix.h$", "<rpc/auth_unix.h>"},
+ {"include/rpc/key_prot.h$", "<rpc/key_prot.h>"},
+ {"include/rpc/pmap_prot.h$", "<rpc/pmap_prot.h>"},
+ {"include/rpc/auth.h$", "<rpc/auth.h>"},
+ {"include/rpc/svc_auth.h$", "<rpc/svc_auth.h>"},
+ {"include/rpc/xdr.h$", "<rpc/xdr.h>"},
+ {"include/rpc/pmap_rmt.h$", "<rpc/pmap_rmt.h>"},
+ {"include/rpc/des_crypt.h$", "<rpc/des_crypt.h>"},
+ {"include/rpc/svc.h$", "<rpc/svc.h>"},
+ {"include/rpc/rpc_des.h$", "<rpc/rpc_des.h>"},
+ {"include/rpc/clnt.h$", "<rpc/clnt.h>"},
+ {"include/scsi/scsi.h$", "<scsi/scsi.h>"},
+ {"include/scsi/sg.h$", "<scsi/sg.h>"},
+ {"include/scsi/scsi_ioctl.h$", "<scsi/scsi_ioctl>"},
+ {"include/netrose/rose.h$", "<netrose/rose.h>"},
+ {"include/nfs/nfs.h$", "<nfs/nfs.h>"},
+ {"include/netatalk/at.h$", "<netatalk/at.h>"},
+ {"include/netinet/ether.h$", "<netinet/ether.h>"},
+ {"include/netinet/icmp6.h$", "<netinet/icmp6.h>"},
+ {"include/netinet/if_ether.h$", "<netinet/if_ether.h>"},
+ {"include/netinet/if_fddi.h$", "<netinet/if_fddi.h>"},
+ {"include/netinet/if_tr.h$", "<netinet/if_tr.h>"},
+ {"include/netinet/igmp.h$", "<netinet/igmp.h>"},
+ {"include/netinet/in.h$", "<netinet/in.h>"},
+ {"include/netinet/in_systm.h$", "<netinet/in_systm.h>"},
+ {"include/netinet/ip.h$", "<netinet/ip.h>"},
+ {"include/netinet/ip6.h$", "<netinet/ip6.h>"},
+ {"include/netinet/ip_icmp.h$", "<netinet/ip_icmp.h>"},
+ {"include/netinet/tcp.h$", "<netinet/tcp.h>"},
+ {"include/netinet/udp.h$", "<netinet/udp.h>"},
+ {"include/netrom/netrom.h$", "<netrom/netrom.h>"},
+ {"include/protocols/routed.h$", "<protocols/routed.h>"},
+ {"include/protocols/rwhod.h$", "<protocols/rwhod.h>"},
+ {"include/protocols/talkd.h$", "<protocols/talkd.h>"},
+ {"include/protocols/timed.h$", "<protocols/timed.h>"},
+ {"include/rpcsvc/klm_prot.x$", "<rpcsvc/klm_prot.x>"},
+ {"include/rpcsvc/rstat.h$", "<rpcsvc/rstat.h>"},
+ {"include/rpcsvc/spray.x$", "<rpcsvc/spray.x>"},
+ {"include/rpcsvc/nlm_prot.x$", "<rpcsvc/nlm_prot.x>"},
+ {"include/rpcsvc/nis_callback.x$", "<rpcsvc/nis_callback.x>"},
+ {"include/rpcsvc/yp.h$", "<rpcsvc/yp.h>"},
+ {"include/rpcsvc/yp.x$", "<rpcsvc/yp.x>"},
+ {"include/rpcsvc/nfs_prot.h$", "<rpcsvc/nfs_prot.h>"},
+ {"include/rpcsvc/rex.h$", "<rpcsvc/rex.h>"},
+ {"include/rpcsvc/yppasswd.h$", "<rpcsvc/yppasswd.h>"},
+ {"include/rpcsvc/rex.x$", "<rpcsvc/rex.x>"},
+ {"include/rpcsvc/nis_tags.h$", "<rpcsvc/nis_tags.h>"},
+ {"include/rpcsvc/nis_callback.h$", "<rpcsvc/nis_callback.h>"},
+ {"include/rpcsvc/nfs_prot.x$", "<rpcsvc/nfs_prot.x>"},
+ {"include/rpcsvc/bootparam_prot.x$", "<rpcsvc/bootparam_prot.x>"},
+ {"include/rpcsvc/rusers.x$", "<rpcsvc/rusers.x>"},
+ {"include/rpcsvc/rquota.x$", "<rpcsvc/rquota.x>"},
+ {"include/rpcsvc/nis.h$", "<rpcsvc/nis.h>"},
+ {"include/rpcsvc/nislib.h$", "<rpcsvc/nislib.h>"},
+ {"include/rpcsvc/ypupd.h$", "<rpcsvc/ypupd.h>"},
+ {"include/rpcsvc/bootparam.h$", "<rpcsvc/bootparam.h>"},
+ {"include/rpcsvc/spray.h$", "<rpcsvc/spray.h>"},
+ {"include/rpcsvc/key_prot.h$", "<rpcsvc/key_prot.h>"},
+ {"include/rpcsvc/klm_prot.h$", "<rpcsvc/klm_prot.h>"},
+ {"include/rpcsvc/sm_inter.h$", "<rpcsvc/sm_inter.h>"},
+ {"include/rpcsvc/nlm_prot.h$", "<rpcsvc/nlm_prot.h>"},
+ {"include/rpcsvc/yp_prot.h$", "<rpcsvc/yp_prot.h>"},
+ {"include/rpcsvc/ypclnt.h$", "<rpcsvc/ypclnt.h>"},
+ {"include/rpcsvc/rstat.x$", "<rpcsvc/rstat.x>"},
+ {"include/rpcsvc/rusers.h$", "<rpcsvc/rusers.h>"},
+ {"include/rpcsvc/key_prot.x$", "<rpcsvc/key_prot.x>"},
+ {"include/rpcsvc/sm_inter.x$", "<rpcsvc/sm_inter.x>"},
+ {"include/rpcsvc/rquota.h$", "<rpcsvc/rquota.h>"},
+ {"include/rpcsvc/nis.x$", "<rpcsvc/nis.x>"},
+ {"include/rpcsvc/bootparam_prot.h$", "<rpcsvc/bootparam_prot.h>"},
+ {"include/rpcsvc/mount.h$", "<rpcsvc/mount.h>"},
+ {"include/rpcsvc/mount.x$", "<rpcsvc/mount.x>"},
+ {"include/rpcsvc/nis_object.x$", "<rpcsvc/nis_object.x>"},
+ {"include/rpcsvc/yppasswd.x$", "<rpcsvc/yppasswd.x>"},
+ {"sys/acct.h$", "<sys/acct.h>"},
+ {"sys/auxv.h$", "<sys/auxv.h>"},
+ {"sys/cdefs.h$", "<sys/cdefs.h>"},
+ {"sys/debugreg.h$", "<sys/debugreg.h>"},
+ {"sys/dir.h$", "<sys/dir.h>"},
+ {"sys/elf.h$", "<sys/elf.h>"},
+ {"sys/epoll.h$", "<sys/epoll.h>"},
+ {"sys/eventfd.h$", "<sys/eventfd.h>"},
+ {"sys/fanotify.h$", "<sys/fanotify.h>"},
+ {"sys/file.h$", "<sys/file.h>"},
+ {"sys/fsuid.h$", "<sys/fsuid.h>"},
+ {"sys/gmon.h$", "<sys/gmon.h>"},
+ {"sys/gmon_out.h$", "<sys/gmon_out.h>"},
+ {"sys/inotify.h$", "<sys/inotify.h>"},
+ {"sys/io.h$", "<sys/io.h>"},
+ {"sys/ioctl.h$", "<sys/ioctl.h>"},
+ {"sys/ipc.h$", "<sys/ipc.h>"},
+ {"sys/kd.h$", "<sys/kd.h>"},
+ {"sys/kdaemon.h$", "<sys/kdaemon.h>"},
+ {"sys/klog.h$", "<sys/klog.h>"},
+ {"sys/mman.h$", "<sys/mman.h>"},
+ {"sys/mount.h$", "<sys/mount.h>"},
+ {"sys/msg.h$", "<sys/msg.h>"},
+ {"sys/mtio.h$", "<sys/mtio.h>"},
+ {"sys/param.h$", "<sys/param.h>"},
+ {"sys/pci.h$", "<sys/pci.h>"},
+ {"sys/perm.h$", "<sys/perm.h>"},
+ {"sys/personality.h$", "<sys/personality.h>"},
+ {"sys/poll.h$", "<sys/poll.h>"},
+ {"sys/prctl.h$", "<sys/prctl.h>"},
+ {"sys/procfs.h$", "<sys/procfs.h>"},
+ {"sys/profil.h$", "<sys/profil.h>"},
+ {"sys/ptrace.h$", "<sys/ptrace.h>"},
+ {"sys/queue.h$", "<sys/queue.h>"},
+ {"sys/quota.h$", "<sys/quota.h>"},
+ {"sys/raw.h$", "<sys/raw.h>"},
+ {"sys/reboot.h$", "<sys/reboot.h>"},
+ {"sys/reg.h$", "<sys/reg.h>"},
+ {"sys/resource.h$", "<sys/resource.h>"},
+ {"sys/select.h$", "<sys/select.h>"},
+ {"sys/sem.h$", "<sys/sem.h>"},
+ {"sys/sendfile.h$", "<sys/sendfile.h>"},
+ {"sys/shm.h$", "<sys/shm.h>"},
+ {"sys/signalfd.h$", "<sys/signalfd.h>"},
+ {"sys/socket.h$", "<sys/socket.h>"},
+ {"sys/stat.h$", "<sys/stat.h>"},
+ {"sys/statfs.h$", "<sys/statfs.h>"},
+ {"sys/statvfs.h$", "<sys/statvfs.h>"},
+ {"sys/swap.h$", "<sys/swap.h>"},
+ {"sys/syscall.h$", "<sys/syscall.h>"},
+ {"sys/sysctl.h$", "<sys/sysctl.h>"},
+ {"sys/sysinfo.h$", "<sys/sysinfo.h>"},
+ {"sys/syslog.h$", "<sys/syslog.h>"},
+ {"sys/sysmacros.h$", "<sys/sysmacros.h>"},
+ {"sys/termios.h$", "<sys/termios.h>"},
+ {"sys/time.h$", "<sys/select.h>"},
+ {"sys/timeb.h$", "<sys/timeb.h>"},
+ {"sys/timerfd.h$", "<sys/timerfd.h>"},
+ {"sys/times.h$", "<sys/times.h>"},
+ {"sys/timex.h$", "<sys/timex.h>"},
+ {"sys/ttychars.h$", "<sys/ttychars.h>"},
+ {"sys/ttydefaults.h$", "<sys/ttydefaults.h>"},
+ {"sys/types.h$", "<sys/types.h>"},
+ {"sys/ucontext.h$", "<sys/ucontext.h>"},
+ {"sys/uio.h$", "<sys/uio.h>"},
+ {"sys/un.h$", "<sys/un.h>"},
+ {"sys/user.h$", "<sys/user.h>"},
+ {"sys/ustat.h$", "<sys/ustat.h>"},
+ {"sys/utsname.h$", "<sys/utsname.h>"},
+ {"sys/vlimit.h$", "<sys/vlimit.h>"},
+ {"sys/vm86.h$", "<sys/vm86.h>"},
+ {"sys/vtimes.h$", "<sys/vtimes.h>"},
+ {"sys/wait.h$", "<sys/wait.h>"},
+ {"sys/xattr.h$", "<sys/xattr.h>"},
+ {"bits/epoll.h$", "<sys/epoll.h>"},
+ {"bits/eventfd.h$", "<sys/eventfd.h>"},
+ {"bits/inotify.h$", "<sys/inotify.h>"},
+ {"bits/ipc.h$", "<sys/ipc.h>"},
+ {"bits/ipctypes.h$", "<sys/ipc.h>"},
+ {"bits/mman-linux.h$", "<sys/mman.h>"},
+ {"bits/mman.h$", "<sys/mman.h>"},
+ {"bits/msq.h$", "<sys/msg.h>"},
+ {"bits/resource.h$", "<sys/resource.h>"},
+ {"bits/sem.h$", "<sys/sem.h>"},
+ {"bits/shm.h$", "<sys/shm.h>"},
+ {"bits/signalfd.h$", "<sys/signalfd.h>"},
+ {"bits/statfs.h$", "<sys/statfs.h>"},
+ {"bits/statvfs.h$", "<sys/statvfs.h>"},
+ {"bits/timerfd.h$", "<sys/timerfd.h>"},
+ {"bits/utsname.h$", "<sys/utsname.h>"},
+ {"bits/auxv.h$", "<sys/auxv.h>"},
+ {"bits/byteswap-16.h$", "<byteswap.h>"},
+ {"bits/byteswap.h$", "<byteswap.h>"},
+ {"bits/confname.h$", "<unistd.h>"},
+ {"bits/dirent.h$", "<dirent.h>"},
+ {"bits/dlfcn.h$", "<dlfcn.h>"},
+ {"bits/elfclass.h$", "<link.h>"},
+ {"bits/endian.h$", "<endian.h>"},
+ {"bits/environments.h$", "<unistd.h>"},
+ {"bits/fcntl-linux.h$", "<fcntl.h>"},
+ {"bits/fcntl.h$", "<fcntl.h>"},
+ {"bits/in.h$", "<netinet/in.h>"},
+ {"bits/ioctl-types.h$", "<sys/ioctl.h>"},
+ {"bits/ioctls.h$", "<sys/ioctl.h>"},
+ {"bits/link.h$", "<link.h>"},
+ {"bits/mqueue.h$", "<mqueue.h>"},
+ {"bits/netdb.h$", "<netdb.h>"},
+ {"bits/param.h$", "<sys/param.h>"},
+ {"bits/poll.h$", "<sys/poll.h>"},
+ {"bits/posix_opt.h$", "<bits/posix_opt.h>"},
+ {"bits/pthreadtypes.h$", "<pthread.h>"},
+ {"bits/sched.h$", "<sched.h>"},
+ {"bits/select.h$", "<sys/select.h>"},
+ {"bits/semaphore.h$", "<semaphore.h>"},
+ {"bits/sigthread.h$", "<pthread.h>"},
+ {"bits/sockaddr.h$", "<sys/socket.h>"},
+ {"bits/socket.h$", "<sys/socket.h>"},
+ {"bits/socket_type.h$", "<sys/socket.h>"},
+ {"bits/stab.def$", "<stab.h>"},
+ {"bits/stat.h$", "<sys/stat.h>"},
+ {"bits/stropts.h$", "<stropts.h>"},
+ {"bits/syscall.h$", "<sys/syscall.h>"},
+ {"bits/syslog-path.h$", "<sys/syslog.h>"},
+ {"bits/termios.h$", "<termios.h>"},
+ {"bits/types.h$", "<sys/types.h>"},
+ {"bits/typesizes.h$", "<sys/types.h>"},
+ {"bits/uio.h$", "<sys/uio.h>"},
+ {"bits/ustat.h$", "<sys/ustat.h>"},
+ {"bits/utmp.h$", "<utmp.h>"},
+ {"bits/utmpx.h$", "<utmpx.h>"},
+ {"bits/waitflags.h$", "<sys/wait.h>"},
+ {"bits/waitstatus.h$", "<sys/wait.h>"},
+ {"bits/xtitypes.h$", "<stropts.h>"},
+ };
+ return &STLPostfixHeaderMap;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- STLPostfixHeaderMap.h - hardcoded header map for STL ----*- 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_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+
+#include "HeaderMapCollector.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap();
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
--- /dev/null
+//===-- SymbolInfo.cpp - Symbol Info ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolInfo.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::yaml::MappingTraits;
+using llvm::yaml::IO;
+using llvm::yaml::Input;
+using ContextType = clang::find_all_symbols::SymbolInfo::ContextType;
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolAndSignals)
+LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<SymbolAndSignals> {
+ static void mapping(IO &io, SymbolAndSignals &Symbol) {
+ io.mapRequired("Name", Symbol.Symbol.Name);
+ io.mapRequired("Contexts", Symbol.Symbol.Contexts);
+ io.mapRequired("FilePath", Symbol.Symbol.FilePath);
+ io.mapRequired("Type", Symbol.Symbol.Type);
+ io.mapRequired("Seen", Symbol.Signals.Seen);
+ io.mapRequired("Used", Symbol.Signals.Used);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<ContextType> {
+ static void enumeration(IO &io, ContextType &value) {
+ io.enumCase(value, "Record", ContextType::Record);
+ io.enumCase(value, "Namespace", ContextType::Namespace);
+ io.enumCase(value, "EnumDecl", ContextType::EnumDecl);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<SymbolKind> {
+ static void enumeration(IO &io, SymbolKind &value) {
+ io.enumCase(value, "Variable", SymbolKind::Variable);
+ io.enumCase(value, "Function", SymbolKind::Function);
+ io.enumCase(value, "Class", SymbolKind::Class);
+ io.enumCase(value, "TypedefName", SymbolKind::TypedefName);
+ io.enumCase(value, "EnumDecl", SymbolKind::EnumDecl);
+ io.enumCase(value, "EnumConstantDecl", SymbolKind::EnumConstantDecl);
+ io.enumCase(value, "Macro", SymbolKind::Macro);
+ io.enumCase(value, "Unknown", SymbolKind::Unknown);
+ }
+};
+
+template <> struct MappingTraits<SymbolInfo::Context> {
+ static void mapping(IO &io, SymbolInfo::Context &Context) {
+ io.mapRequired("ContextType", Context.first);
+ io.mapRequired("ContextName", Context.second);
+ }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace find_all_symbols {
+
+SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
+ llvm::StringRef FilePath,
+ const std::vector<Context> &Contexts)
+ : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts) {}
+
+bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
+ return std::tie(Name, Type, FilePath, Contexts) ==
+ std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+bool SymbolInfo::operator<(const SymbolInfo &Symbol) const {
+ return std::tie(Name, Type, FilePath, Contexts) <
+ std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+std::string SymbolInfo::getQualifiedName() const {
+ std::string QualifiedName = Name;
+ for (const auto &Context : Contexts) {
+ if (Context.first == ContextType::EnumDecl)
+ continue;
+ QualifiedName = Context.second + "::" + QualifiedName;
+ }
+ return QualifiedName;
+}
+
+SymbolInfo::Signals &SymbolInfo::Signals::operator+=(const Signals &RHS) {
+ Seen += RHS.Seen;
+ Used += RHS.Used;
+ return *this;
+}
+
+SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals &RHS) const {
+ Signals Result = *this;
+ Result += RHS;
+ return Result;
+}
+
+bool SymbolInfo::Signals::operator==(const Signals &RHS) const {
+ return std::tie(Seen, Used) == std::tie(RHS.Seen, RHS.Used);
+}
+
+bool SymbolAndSignals::operator==(const SymbolAndSignals& RHS) const {
+ return std::tie(Symbol, Signals) == std::tie(RHS.Symbol, RHS.Signals);
+}
+
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+ const SymbolInfo::SignalMap &Symbols) {
+ llvm::yaml::Output yout(OS);
+ for (const auto &Symbol : Symbols) {
+ SymbolAndSignals S{Symbol.first, Symbol.second};
+ yout << S;
+ }
+ return true;
+}
+
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
+ std::vector<SymbolAndSignals> Symbols;
+ llvm::yaml::Input yin(Yaml);
+ yin >> Symbols;
+ return Symbols;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===-- SymbolInfo.h - Symbol Info ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+/// \brief Describes a named symbol from a header.
+/// Symbols with the same qualified name and type (e.g. function overloads)
+/// that appear in the same header are represented by a single SymbolInfo.
+///
+/// TODO: keep track of instances, e.g. overload locations and signatures.
+class SymbolInfo {
+public:
+ /// \brief The SymbolInfo Type.
+ enum class SymbolKind {
+ Function,
+ Class,
+ Variable,
+ TypedefName,
+ EnumDecl,
+ EnumConstantDecl,
+ Macro,
+ Unknown,
+ };
+
+ /// \brief The Context Type.
+ enum class ContextType {
+ Namespace, // Symbols declared in a namespace.
+ Record, // Symbols declared in a class.
+ EnumDecl, // Enum constants declared in a enum declaration.
+ };
+
+ /// \brief A pair of <ContextType, ContextName>.
+ typedef std::pair<ContextType, std::string> Context;
+
+ // \brief Signals are signals gathered by observing how a symbol is used.
+ // These are used to rank results.
+ struct Signals {
+ Signals() {}
+ Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
+
+ // Number of times this symbol was visible to a TU.
+ unsigned Seen = 0;
+
+ // Number of times this symbol was referenced a TU's main file.
+ unsigned Used = 0;
+
+ Signals &operator+=(const Signals &RHS);
+ Signals operator+(const Signals &RHS) const;
+ bool operator==(const Signals &RHS) const;
+ };
+
+ using SignalMap = std::map<SymbolInfo, Signals>;
+
+ // The default constructor is required by YAML traits in
+ // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
+ SymbolInfo() : Type(SymbolKind::Unknown) {}
+
+ SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
+ const std::vector<Context> &Contexts);
+
+ void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
+
+ /// \brief Get symbol name.
+ llvm::StringRef getName() const { return Name; }
+
+ /// \brief Get the fully-qualified symbol name.
+ std::string getQualifiedName() const;
+
+ /// \brief Get symbol type.
+ SymbolKind getSymbolKind() const { return Type; }
+
+ /// \brief Get a relative file path where symbol comes from.
+ llvm::StringRef getFilePath() const { return FilePath; }
+
+ /// \brief Get symbol contexts.
+ const std::vector<SymbolInfo::Context> &getContexts() const {
+ return Contexts;
+ }
+
+ bool operator<(const SymbolInfo &Symbol) const;
+
+ bool operator==(const SymbolInfo &Symbol) const;
+
+private:
+ friend struct llvm::yaml::MappingTraits<struct SymbolAndSignals>;
+
+ /// \brief Identifier name.
+ std::string Name;
+
+ /// \brief Symbol type.
+ SymbolKind Type;
+
+ /// \brief The file path where the symbol comes from. It's a relative file
+ /// path based on the build directory.
+ std::string FilePath;
+
+ /// \brief Contains information about symbol contexts. Context information is
+ /// stored from the inner-most level to outer-most level.
+ ///
+ /// For example, if a symbol 'x' is declared as:
+ /// namespace na { namespace nb { class A { int x; } } }
+ /// The contexts would be { {RECORD, "A"}, {NAMESPACE, "nb"}, {NAMESPACE,
+ /// "na"} }.
+ /// The name of an anonymous namespace is "".
+ ///
+ /// If the symbol is declared in `TranslationUnitDecl`, it has no context.
+ std::vector<Context> Contexts;
+};
+
+struct SymbolAndSignals {
+ SymbolInfo Symbol;
+ SymbolInfo::Signals Signals;
+ bool operator==(const SymbolAndSignals& RHS) const;
+};
+
+/// \brief Write SymbolInfos to a stream (YAML format).
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+ const SymbolInfo::SignalMap &Symbols);
+
+/// \brief Read SymbolInfos from a YAML document.
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
--- /dev/null
+//===--- SymbolReporter.h - Symbol Reporter ---------------------*- 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_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+
+#include "SymbolInfo.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief An interface for classes that collect symbols.
+class SymbolReporter {
+public:
+ virtual ~SymbolReporter() = default;
+
+ virtual void reportSymbols(llvm::StringRef FileName,
+ const SymbolInfo::SignalMap &Symbols) = 0;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(find-all-symbols
+ FindAllSymbolsMain.cpp
+ )
+
+target_link_libraries(find-all-symbols
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTooling
+ findAllSymbols
+ )
+
+install(TARGETS find-all-symbols
+ RUNTIME DESTINATION bin)
+
+install(PROGRAMS run-find-all-symbols.py
+ DESTINATION share/clang
+ COMPONENT find-all-symbols)
--- /dev/null
+//===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "STLPostfixHeaderMap.h"
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+#include <mutex>
+#include <set>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace clang::tooling;
+using namespace llvm;
+using SymbolInfo = clang::find_all_symbols::SymbolInfo;
+
+// Apply a custom category to all command-line options so that they are the
+// only ones displayed.
+static cl::OptionCategory FindAllSymbolsCategory("find_all_symbols options");
+
+// CommonOptionsParser declares HelpMessage with a description of the common
+// command-line options related to the compilation database and input files.
+// It's nice to have this help message in all tools.
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+
+// A help message for this specific tool can be added afterwards.
+static cl::extrahelp MoreHelp("\nMore help text...");
+
+static cl::opt<std::string> OutputDir("output-dir", cl::desc(R"(
+The output directory for saving the results.)"),
+ cl::init("."),
+ cl::cat(FindAllSymbolsCategory));
+
+static cl::opt<std::string> MergeDir("merge-dir", cl::desc(R"(
+The directory for merging symbols.)"),
+ cl::init(""),
+ cl::cat(FindAllSymbolsCategory));
+namespace clang {
+namespace find_all_symbols {
+
+class YamlReporter : public SymbolReporter {
+public:
+ void reportSymbols(StringRef FileName,
+ const SymbolInfo::SignalMap &Symbols) override {
+ int FD;
+ SmallString<128> ResultPath;
+ llvm::sys::fs::createUniqueFile(
+ OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
+ FD, ResultPath);
+ llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+ WriteSymbolInfosToStream(OS, Symbols);
+ }
+};
+
+bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
+ std::error_code EC;
+ SymbolInfo::SignalMap Symbols;
+ std::mutex SymbolMutex;
+ auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
+ // Synchronize set accesses.
+ std::unique_lock<std::mutex> LockGuard(SymbolMutex);
+ for (const auto &Symbol : NewSymbols) {
+ Symbols[Symbol.Symbol] += Symbol.Signals;
+ }
+ };
+
+ // Load all symbol files in MergeDir.
+ {
+ llvm::ThreadPool Pool;
+ for (llvm::sys::fs::directory_iterator Dir(MergeDir, EC), DirEnd;
+ Dir != DirEnd && !EC; Dir.increment(EC)) {
+ // Parse YAML files in parallel.
+ Pool.async(
+ [&AddSymbols](std::string Path) {
+ auto Buffer = llvm::MemoryBuffer::getFile(Path);
+ if (!Buffer) {
+ llvm::errs() << "Can't open " << Path << "\n";
+ return;
+ }
+ std::vector<SymbolAndSignals> Symbols =
+ ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
+ for (auto &Symbol : Symbols) {
+ // Only count one occurrence per file, to avoid spam.
+ Symbol.Signals.Seen = std::min(Symbol.Signals.Seen, 1u);
+ Symbol.Signals.Used = std::min(Symbol.Signals.Used, 1u);
+ }
+ // FIXME: Merge without creating such a heavy contention point.
+ AddSymbols(Symbols);
+ },
+ Dir->path());
+ }
+ }
+
+ llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Can't open '" << OutputFile << "': " << EC.message()
+ << '\n';
+ return false;
+ }
+ WriteSymbolInfosToStream(OS, Symbols);
+ return true;
+}
+
+} // namespace clang
+} // namespace find_all_symbols
+
+int main(int argc, const char **argv) {
+ CommonOptionsParser OptionsParser(argc, argv, FindAllSymbolsCategory);
+ ClangTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+
+ std::vector<std::string> sources = OptionsParser.getSourcePathList();
+ if (sources.empty()) {
+ llvm::errs() << "Must specify at least one one source file.\n";
+ return 1;
+ }
+ if (!MergeDir.empty()) {
+ clang::find_all_symbols::Merge(MergeDir, sources[0]);
+ return 0;
+ }
+
+ clang::find_all_symbols::YamlReporter Reporter;
+
+ auto Factory =
+ llvm::make_unique<clang::find_all_symbols::FindAllSymbolsActionFactory>(
+ &Reporter, clang::find_all_symbols::getSTLPostfixHeaderMap());
+ return Tool.run(Factory.get());
+}
--- /dev/null
+#!/usr/bin/env python
+#
+#=- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python -*-=#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+"""
+Parallel find-all-symbols runner
+================================
+
+Runs find-all-symbols over all files in a compilation database.
+
+Example invocations.
+- Run find-all-symbols on all files in the current working directory.
+ run-find-all-symbols.py <source-file>
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+
+
+def find_compilation_database(path):
+ """Adjusts the directory until a compilation database is found."""
+ result = './'
+ while not os.path.isfile(os.path.join(result, path)):
+ if os.path.realpath(result) == '/':
+ print 'Error: could not find compilation database.'
+ sys.exit(1)
+ result += '../'
+ return os.path.realpath(result)
+
+
+def MergeSymbols(directory, args):
+ """Merge all symbol files (yaml) in a given directaory into a single file."""
+ invocation = [args.binary, '-merge-dir='+directory, args.saving_path]
+ subprocess.call(invocation)
+ print 'Merge is finished. Saving results in ' + args.saving_path
+
+
+def run_find_all_symbols(args, tmpdir, build_path, queue):
+ """Takes filenames out of queue and runs find-all-symbols on them."""
+ while True:
+ name = queue.get()
+ invocation = [args.binary, name, '-output-dir='+tmpdir, '-p='+build_path]
+ sys.stdout.write(' '.join(invocation) + '\n')
+ subprocess.call(invocation)
+ queue.task_done()
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Runs find-all-symbols over all'
+ 'files in a compilation database.')
+ parser.add_argument('-binary', metavar='PATH',
+ default='./bin/find-all-symbols',
+ help='path to find-all-symbols binary')
+ parser.add_argument('-j', type=int, default=0,
+ help='number of instances to be run in parallel.')
+ parser.add_argument('-p', dest='build_path',
+ help='path used to read a compilation database.')
+ parser.add_argument('-saving-path', default='./find_all_symbols_db.yaml',
+ help='result saving path')
+ args = parser.parse_args()
+
+ db_path = 'compile_commands.json'
+
+ if args.build_path is not None:
+ build_path = args.build_path
+ else:
+ build_path = find_compilation_database(db_path)
+
+ tmpdir = tempfile.mkdtemp()
+
+ # Load the database and extract all files.
+ database = json.load(open(os.path.join(build_path, db_path)))
+ files = [entry['file'] for entry in database]
+
+ max_task = args.j
+ if max_task == 0:
+ max_task = multiprocessing.cpu_count()
+
+ try:
+ # Spin up a bunch of tidy-launching threads.
+ queue = Queue.Queue(max_task)
+ for _ in range(max_task):
+ t = threading.Thread(target=run_find_all_symbols,
+ args=(args, tmpdir, build_path, queue))
+ t.daemon = True
+ t.start()
+
+ # Fill the queue with files.
+ for name in files:
+ queue.put(name)
+
+ # Wait for all threads to be done.
+ queue.join()
+
+ MergeSymbols(tmpdir, args)
+
+
+ except KeyboardInterrupt:
+ # This is a sad hack. Unfortunately subprocess goes
+ # bonkers with ctrl-c and we start forking merrily.
+ print '\nCtrl-C detected, goodbye.'
+ os.kill(0, 9)
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+add_clang_library(clangIncludeFixerPlugin
+ IncludeFixerPlugin.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFrontend
+ clangIncludeFixer
+ clangParse
+ clangSema
+ clangTooling
+ ${LLVM_PTHREAD_LIB}
+ )
--- /dev/null
+//===- IncludeFixerPlugin.cpp - clang-include-fixer as a clang plugin -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../IncludeFixer.h"
+#include "../YamlSymbolIndex.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace include_fixer {
+
+/// The core include fixer plugin action. This just provides the AST consumer
+/// and command line flag parsing for using include fixer as a clang plugin.
+class ClangIncludeFixerPluginAction : public PluginASTAction {
+ /// ASTConsumer to keep the symbol index alive. We don't really need an
+ /// ASTConsumer for this plugin (everything is funneled on the side through
+ /// Sema) but we have to keep the symbol index alive until sema is done.
+ struct ASTConsumerManagerWrapper : public ASTConsumer {
+ ASTConsumerManagerWrapper(std::shared_ptr<SymbolIndexManager> SIM)
+ : SymbolIndexMgr(std::move(SIM)) {}
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ };
+
+public:
+ explicit ClangIncludeFixerPluginAction()
+ : SymbolIndexMgr(std::make_shared<SymbolIndexManager>()),
+ SemaSource(new IncludeFixerSemaSource(*SymbolIndexMgr,
+ /*MinimizeIncludePaths=*/true,
+ /*GenerateDiagnostics=*/true)) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
+ CI.setExternalSemaSource(SemaSource);
+ SemaSource->setFilePath(InFile);
+ SemaSource->setCompilerInstance(&CI);
+ return llvm::make_unique<ASTConsumerManagerWrapper>(SymbolIndexMgr);
+ }
+
+ void ExecuteAction() override {} // Do nothing.
+
+ bool ParseArgs(const CompilerInstance &CI,
+ const std::vector<std::string> &Args) override {
+ StringRef DB = "yaml";
+ StringRef Input;
+
+ // Parse the extra command line args.
+ // FIXME: This is very limited at the moment.
+ for (StringRef Arg : Args) {
+ if (Arg.startswith("-db="))
+ DB = Arg.substr(strlen("-db="));
+ else if (Arg.startswith("-input="))
+ Input = Arg.substr(strlen("-input="));
+ }
+
+ std::string InputFile = CI.getFrontendOpts().Inputs[0].getFile();
+ auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> SymbolIdx(
+ nullptr);
+ if (DB == "yaml") {
+ if (!Input.empty()) {
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromFile(Input);
+ } else {
+ // If we don't have any input file, look in the directory of the first
+ // file and its parents.
+ SmallString<128> AbsolutePath(tooling::getAbsolutePath(InputFile));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromDirectory(
+ Directory, "find_all_symbols_db.yaml");
+ }
+ }
+ return std::move(*SymbolIdx);
+ };
+
+ SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+ return true;
+ }
+
+private:
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource;
+};
+} // namespace include_fixer
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the include fixer plugin.
+volatile int ClangIncludeFixerPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<
+ clang::include_fixer::ClangIncludeFixerPluginAction>
+ X("clang-include-fixer", "clang-include-fixer");
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-include-fixer
+ ClangIncludeFixer.cpp
+ )
+
+target_link_libraries(clang-include-fixer
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangIncludeFixer
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ findAllSymbols
+ )
+
+install(TARGETS clang-include-fixer
+ RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-include-fixer.el
+ DESTINATION share/clang
+ COMPONENT clang-include-fixer)
+install(PROGRAMS clang-include-fixer.py
+ DESTINATION share/clang
+ COMPONENT clang-include-fixer)
--- /dev/null
+//===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzySymbolIndex.h"
+#include "InMemorySymbolIndex.h"
+#include "IncludeFixer.h"
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "YamlSymbolIndex.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+using clang::include_fixer::IncludeFixerContext;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(IncludeFixerContext)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::QuerySymbolInfo)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<tooling::Range> {
+ struct NormalizedRange {
+ NormalizedRange(const IO &) : Offset(0), Length(0) {}
+
+ NormalizedRange(const IO &, const tooling::Range &R)
+ : Offset(R.getOffset()), Length(R.getLength()) {}
+
+ tooling::Range denormalize(const IO &) {
+ return tooling::Range(Offset, Length);
+ }
+
+ unsigned Offset;
+ unsigned Length;
+ };
+ static void mapping(IO &IO, tooling::Range &Info) {
+ MappingNormalization<NormalizedRange, tooling::Range> Keys(IO, Info);
+ IO.mapRequired("Offset", Keys->Offset);
+ IO.mapRequired("Length", Keys->Length);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::HeaderInfo> {
+ static void mapping(IO &io, IncludeFixerContext::HeaderInfo &Info) {
+ io.mapRequired("Header", Info.Header);
+ io.mapRequired("QualifiedName", Info.QualifiedName);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::QuerySymbolInfo> {
+ static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &Info) {
+ io.mapRequired("RawIdentifier", Info.RawIdentifier);
+ io.mapRequired("Range", Info.Range);
+ }
+};
+
+template <> struct MappingTraits<IncludeFixerContext> {
+ static void mapping(IO &IO, IncludeFixerContext &Context) {
+ IO.mapRequired("QuerySymbolInfos", Context.QuerySymbolInfos);
+ IO.mapRequired("HeaderInfos", Context.HeaderInfos);
+ IO.mapRequired("FilePath", Context.FilePath);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace {
+cl::OptionCategory IncludeFixerCategory("Tool options");
+
+enum DatabaseFormatTy {
+ fixed, ///< Hard-coded mapping.
+ yaml, ///< Yaml database created by find-all-symbols.
+ fuzzyYaml, ///< Yaml database with fuzzy-matched identifiers.
+};
+
+cl::opt<DatabaseFormatTy> DatabaseFormat(
+ "db", cl::desc("Specify input format"),
+ cl::values(clEnumVal(fixed, "Hard-coded mapping"),
+ clEnumVal(yaml, "Yaml database created by find-all-symbols"),
+ clEnumVal(fuzzyYaml, "Yaml database, with fuzzy-matched names")),
+ cl::init(yaml), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> Input("input",
+ cl::desc("String to initialize the database"),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+ QuerySymbol("query-symbol",
+ cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n"
+ "database directly without parsing the file."),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+ MinimizeIncludePaths("minimize-paths",
+ cl::desc("Whether to minimize added include paths"),
+ cl::init(true), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> Quiet("q", cl::desc("Reduce terminal output"), cl::init(false),
+ cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+ STDINMode("stdin",
+ cl::desc("Override source file's content (in the overlaying\n"
+ "virtual file system) with input from <stdin> and run\n"
+ "the tool on the new content with the compilation\n"
+ "options of the source file. This mode is currently\n"
+ "used for editor integration."),
+ cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> OutputHeaders(
+ "output-headers",
+ cl::desc("Print the symbol being queried and all its relevant headers in\n"
+ "JSON format to stdout:\n"
+ " {\n"
+ " \"FilePath\": \"/path/to/foo.cc\",\n"
+ " \"QuerySymbolInfos\": [\n"
+ " {\"RawIdentifier\": \"foo\",\n"
+ " \"Range\": {\"Offset\": 0, \"Length\": 3}}\n"
+ " ],\n"
+ " \"HeaderInfos\": [ {\"Header\": \"\\\"foo_a.h\\\"\",\n"
+ " \"QualifiedName\": \"a::foo\"} ]\n"
+ " }"),
+ cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> InsertHeader(
+ "insert-header",
+ cl::desc("Insert a specific header. This should run with STDIN mode.\n"
+ "The result is written to stdout. It is currently used for\n"
+ "editor integration. Support YAML/JSON format:\n"
+ " -insert-header=\"{\n"
+ " FilePath: \"/path/to/foo.cc\",\n"
+ " QuerySymbolInfos: [\n"
+ " {RawIdentifier: foo,\n"
+ " Range: {Offset: 0, Length: 3}}\n"
+ " ],\n"
+ " HeaderInfos: [ {Headers: \"\\\"foo_a.h\\\"\",\n"
+ " QualifiedName: \"a::foo\"} ]}\""),
+ cl::init(""), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+ Style("style",
+ cl::desc("Fallback style for reformatting after inserting new\n"
+ "headers if there is no clang-format config file found."),
+ cl::init("llvm"), cl::cat(IncludeFixerCategory));
+
+std::unique_ptr<include_fixer::SymbolIndexManager>
+createSymbolIndexManager(StringRef FilePath) {
+ using find_all_symbols::SymbolInfo;
+
+ auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
+ switch (DatabaseFormat) {
+ case fixed: {
+ // Parse input and fill the database with it.
+ // <symbol>=<header><, header...>
+ // Multiple symbols can be given, separated by semicolons.
+ std::map<std::string, std::vector<std::string>> SymbolsMap;
+ SmallVector<StringRef, 4> SemicolonSplits;
+ StringRef(Input).split(SemicolonSplits, ";");
+ std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+ for (StringRef Pair : SemicolonSplits) {
+ auto Split = Pair.split('=');
+ std::vector<std::string> Headers;
+ SmallVector<StringRef, 4> CommaSplits;
+ Split.second.split(CommaSplits, ",");
+ for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
+ Symbols.push_back(
+ {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
+ CommaSplits[I].trim(), {}),
+ // Use fake "seen" signal for tests, so first header wins.
+ SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
+ /*Used=*/0)});
+ }
+ SymbolIndexMgr->addSymbolIndex([=]() {
+ return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
+ });
+ break;
+ }
+ case yaml: {
+ auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> DB(
+ nullptr);
+ if (!Input.empty()) {
+ DB = include_fixer::YamlSymbolIndex::createFromFile(Input);
+ } else {
+ // If we don't have any input file, look in the directory of the
+ // first
+ // file and its parents.
+ SmallString<128> AbsolutePath(tooling::getAbsolutePath(FilePath));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+ DB = include_fixer::YamlSymbolIndex::createFromDirectory(
+ Directory, "find_all_symbols_db.yaml");
+ }
+
+ if (!DB) {
+ llvm::errs() << "Couldn't find YAML db: " << DB.getError().message()
+ << '\n';
+ return nullptr;
+ }
+ return std::move(*DB);
+ };
+
+ SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+ break;
+ }
+ case fuzzyYaml: {
+ // This mode is not very useful, because we don't correct the identifier.
+ // It's main purpose is to expose FuzzySymbolIndex to tests.
+ SymbolIndexMgr->addSymbolIndex(
+ []() -> std::unique_ptr<include_fixer::SymbolIndex> {
+ auto DB = include_fixer::FuzzySymbolIndex::createFromYAML(Input);
+ if (!DB) {
+ llvm::errs() << "Couldn't load fuzzy YAML db: "
+ << llvm::toString(DB.takeError()) << '\n';
+ return nullptr;
+ }
+ return std::move(*DB);
+ });
+ break;
+ }
+ }
+ return SymbolIndexMgr;
+}
+
+void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
+ OS << "{\n"
+ << " \"FilePath\": \""
+ << llvm::yaml::escape(Context.getFilePath()) << "\",\n"
+ << " \"QuerySymbolInfos\": [\n";
+ for (const auto &Info : Context.getQuerySymbolInfos()) {
+ OS << " {\"RawIdentifier\": \"" << Info.RawIdentifier << "\",\n";
+ OS << " \"Range\":{";
+ OS << "\"Offset\":" << Info.Range.getOffset() << ",";
+ OS << "\"Length\":" << Info.Range.getLength() << "}}";
+ if (&Info != &Context.getQuerySymbolInfos().back())
+ OS << ",\n";
+ }
+ OS << "\n ],\n";
+ OS << " \"HeaderInfos\": [\n";
+ const auto &HeaderInfos = Context.getHeaderInfos();
+ for (const auto &Info : HeaderInfos) {
+ OS << " {\"Header\": \"" << llvm::yaml::escape(Info.Header) << "\",\n"
+ << " \"QualifiedName\": \"" << Info.QualifiedName << "\"}";
+ if (&Info != &HeaderInfos.back())
+ OS << ",\n";
+ }
+ OS << "\n";
+ OS << " ]\n";
+ OS << "}\n";
+}
+
+int includeFixerMain(int argc, const char **argv) {
+ tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
+ tooling::ClangTool tool(options.getCompilations(),
+ options.getSourcePathList());
+
+ llvm::StringRef SourceFilePath = options.getSourcePathList().front();
+ // In STDINMode, we override the file content with the <stdin> input.
+ // Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of
+ // the if-block so that `Code` is not released after the if-block.
+ std::unique_ptr<llvm::MemoryBuffer> Code;
+ if (STDINMode) {
+ assert(options.getSourcePathList().size() == 1 &&
+ "Expect exactly one file path in STDINMode.");
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr =
+ MemoryBuffer::getSTDIN();
+ if (std::error_code EC = CodeOrErr.getError()) {
+ errs() << EC.message() << "\n";
+ return 1;
+ }
+ Code = std::move(CodeOrErr.get());
+ if (Code->getBufferSize() == 0)
+ return 0; // Skip empty files.
+
+ tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
+ }
+
+ if (!InsertHeader.empty()) {
+ if (!STDINMode) {
+ errs() << "Should be running in STDIN mode\n";
+ return 1;
+ }
+
+ llvm::yaml::Input yin(InsertHeader);
+ IncludeFixerContext Context;
+ yin >> Context;
+
+ const auto &HeaderInfos = Context.getHeaderInfos();
+ assert(!HeaderInfos.empty());
+ // We only accept one unique header.
+ // Check all elements in HeaderInfos have the same header.
+ bool IsUniqueHeader = std::equal(
+ HeaderInfos.begin()+1, HeaderInfos.end(), HeaderInfos.begin(),
+ [](const IncludeFixerContext::HeaderInfo &LHS,
+ const IncludeFixerContext::HeaderInfo &RHS) {
+ return LHS.Header == RHS.Header;
+ });
+ if (!IsUniqueHeader) {
+ errs() << "Expect exactly one unique header.\n";
+ return 1;
+ }
+
+ // If a header has multiple symbols, we won't add the missing namespace
+ // qualifiers because we don't know which one is exactly used.
+ //
+ // Check whether all elements in HeaderInfos have the same qualified name.
+ bool IsUniqueQualifiedName = std::equal(
+ HeaderInfos.begin() + 1, HeaderInfos.end(), HeaderInfos.begin(),
+ [](const IncludeFixerContext::HeaderInfo &LHS,
+ const IncludeFixerContext::HeaderInfo &RHS) {
+ return LHS.QualifiedName == RHS.QualifiedName;
+ });
+ auto InsertStyle = format::getStyle("file", Context.getFilePath(), Style);
+ if (!InsertStyle) {
+ llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+ return 1;
+ }
+ auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
+ Code->getBuffer(), Context, *InsertStyle,
+ /*AddQualifiers=*/IsUniqueQualifiedName);
+ if (!Replacements) {
+ errs() << "Failed to create replacements: "
+ << llvm::toString(Replacements.takeError()) << "\n";
+ return 1;
+ }
+
+ auto ChangedCode =
+ tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ return 1;
+ }
+ llvm::outs() << *ChangedCode;
+ return 0;
+ }
+
+ // Set up data source.
+ std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
+ createSymbolIndexManager(SourceFilePath);
+ if (!SymbolIndexMgr)
+ return 1;
+
+ // Query symbol mode.
+ if (!QuerySymbol.empty()) {
+ auto MatchedSymbols = SymbolIndexMgr->search(
+ QuerySymbol, /*IsNestedSearch=*/true, SourceFilePath);
+ for (auto &Symbol : MatchedSymbols) {
+ std::string HeaderPath = Symbol.getFilePath().str();
+ Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<')
+ ? HeaderPath
+ : "\"" + HeaderPath + "\""));
+ }
+
+ // We leave an empty symbol range as we don't know the range of the symbol
+ // being queried in this mode. include-fixer won't add namespace qualifiers
+ // if the symbol range is empty, which also fits this case.
+ IncludeFixerContext::QuerySymbolInfo Symbol;
+ Symbol.RawIdentifier = QuerySymbol;
+ auto Context =
+ IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols);
+ writeToJson(llvm::outs(), Context);
+ return 0;
+ }
+
+ // Now run our tool.
+ std::vector<include_fixer::IncludeFixerContext> Contexts;
+ include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts,
+ Style, MinimizeIncludePaths);
+
+ if (tool.run(&Factory) != 0) {
+ // We suppress all Clang diagnostics (because they would be wrong,
+ // include-fixer does custom recovery) but still want to give some feedback
+ // in case there was a compiler error we couldn't recover from. The most
+ // common case for this is a #include in the file that couldn't be found.
+ llvm::errs() << "Fatal compiler error occurred while parsing file!"
+ " (incorrect include paths?)\n";
+ return 1;
+ }
+
+ assert(!Contexts.empty());
+
+ if (OutputHeaders) {
+ // FIXME: Print contexts of all processing files instead of the first one.
+ writeToJson(llvm::outs(), Contexts.front());
+ return 0;
+ }
+
+ std::vector<tooling::Replacements> FixerReplacements;
+ for (const auto &Context : Contexts) {
+ StringRef FilePath = Context.getFilePath();
+ auto InsertStyle = format::getStyle("file", FilePath, Style);
+ if (!InsertStyle) {
+ llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+ return 1;
+ }
+ auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+ if (!Buffer) {
+ errs() << "Couldn't open file: " + FilePath.str() + ": "
+ << 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);
+ }
+
+ if (!Quiet) {
+ for (const auto &Context : Contexts) {
+ if (!Context.getHeaderInfos().empty()) {
+ llvm::errs() << "Added #include "
+ << Context.getHeaderInfos().front().Header << " for "
+ << Context.getFilePath() << "\n";
+ }
+ }
+ }
+
+ if (STDINMode) {
+ assert(FixerReplacements.size() == 1);
+ auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(),
+ FixerReplacements.front());
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+ return 1;
+ }
+ llvm::outs() << *ChangedCode;
+ return 0;
+ }
+
+ // Set up a new source manager for applying the resulting replacements.
+ IntrusiveRefCntPtr<DiagnosticOptions> 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());
+ for (const auto &Replacement : FixerReplacements) {
+ if (!tooling::applyAllReplacements(Replacement, Rewrites)) {
+ llvm::errs() << "Failed to apply replacements.\n";
+ return 1;
+ }
+ }
+ return Rewrites.overwriteChangedFiles();
+}
+
+} // namespace
+
+int main(int argc, const char **argv) {
+ return includeFixerMain(argc, argv);
+}
--- /dev/null
+;;; clang-include-fixer-test.el --- unit tests for clang-include-fixer.el -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Unit tests for clang-include-fixer.el.
+
+;;; Code:
+
+(require 'clang-include-fixer)
+
+(require 'cc-mode)
+(require 'ert)
+
+(ert-deftest clang-include-fixer--insert-line ()
+ "Unit test for `clang-include-fixer--insert-line'."
+ (with-temp-buffer
+ (insert "aa\nab\nac\nad\n")
+ (let ((from (current-buffer)))
+ (with-temp-buffer
+ (insert "aa\nac\nad\n")
+ (let ((to (current-buffer)))
+ (should (clang-include-fixer--insert-line from to))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n")))))
+ (should (equal (buffer-string) "aa\nab\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--insert-line-diff-on-empty-line ()
+ "Unit test for `clang-include-fixer--insert-line'."
+ (with-temp-buffer
+ (insert "aa\nab\n\nac\nad\n")
+ (let ((from (current-buffer)))
+ (with-temp-buffer
+ (insert "aa\n\nac\nad\n")
+ (let ((to (current-buffer)))
+ (should (clang-include-fixer--insert-line from to))
+ (should (equal (buffer-string) "aa\nab\n\nac\nad\n")))))
+ (should (equal (buffer-string) "aa\nab\n\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--symbol-at-point ()
+ "Unit test for `clang-include-fixer--symbol-at-point'."
+ (with-temp-buffer
+ (insert "a+bbb::cc")
+ (c++-mode)
+ (goto-char (point-min))
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ ;; Emacs treats the character immediately following a symbol as part of the
+ ;; symbol.
+ (should (equal (clang-include-fixer--symbol-at-point) "a"))
+ (forward-char)
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))
+ (goto-char (point-max))
+ (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))))
+
+(ert-deftest clang-include-fixer--highlight ()
+ (with-temp-buffer
+ (insert "util::Status foo;\n")
+ (setq buffer-file-coding-system 'utf-8-unix)
+ (should (equal nil (clang-include-fixer--highlight
+ '((Range . ((Offset . 0) (Length . 0)))))))
+ (let ((overlay (clang-include-fixer--highlight
+ '((Range . ((Offset . 1) (Length . 12)))))))
+ (should (equal 2 (overlay-start overlay)))
+ (should (equal 14 (overlay-end overlay))))))
+
+;;; clang-include-fixer-test.el ends here
--- /dev/null
+;;; clang-include-fixer.el --- Emacs integration of the clang include fixer -*- lexical-binding: t; -*-
+
+;; Keywords: tools, c
+;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))
+
+;;; Commentary:
+
+;; This package allows Emacs users to invoke the 'clang-include-fixer' within
+;; Emacs. 'clang-include-fixer' provides an automated way of adding #include
+;; directives for missing symbols in one translation unit, see
+;; <http://clang.llvm.org/extra/include-fixer.html>.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'json)
+(require 'let-alist)
+
+(defgroup clang-include-fixer nil
+ "Clang-based include fixer."
+ :group 'tools)
+
+(defvar clang-include-fixer-add-include-hook nil
+ "A hook that will be called for every added include.
+The first argument is the filename of the include, the second argument is
+non-nil if the include is a system-header.")
+
+(defcustom clang-include-fixer-executable
+ "clang-include-fixer"
+ "Location of the clang-include-fixer executable.
+
+A string containing the name or the full path of the executable."
+ :group 'clang-include-fixer
+ :type '(file :must-match t)
+ :risky t)
+
+(defcustom clang-include-fixer-input-format
+ 'yaml
+ "Input format for clang-include-fixer.
+This string is passed as -db argument to
+`clang-include-fixer-executable'."
+ :group 'clang-include-fixer
+ :type '(radio
+ (const :tag "Hard-coded mapping" :fixed)
+ (const :tag "YAML" yaml)
+ (symbol :tag "Other"))
+ :risky t)
+
+(defcustom clang-include-fixer-init-string
+ ""
+ "Database initialization string for clang-include-fixer.
+This string is passed as -input argument to
+`clang-include-fixer-executable'."
+ :group 'clang-include-fixer
+ :type 'string
+ :risky t)
+
+(defface clang-include-fixer-highlight '((t :background "green"))
+ "Used for highlighting the symbol for which a header file is being added.")
+
+;;;###autoload
+(defun clang-include-fixer ()
+ "Invoke the Include Fixer to insert missing C++ headers."
+ (interactive)
+ (message (concat "Calling the include fixer. "
+ "This might take some seconds. Please wait."))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ "-output-headers"))
+
+;;;###autoload
+(defun clang-include-fixer-at-point ()
+ "Invoke the Clang include fixer for the symbol at point."
+ (interactive)
+ (let ((symbol (clang-include-fixer--symbol-at-point)))
+ (unless symbol
+ (user-error "No symbol at current location"))
+ (clang-include-fixer-from-symbol symbol)))
+
+;;;###autoload
+(defun clang-include-fixer-from-symbol (symbol)
+ "Invoke the Clang include fixer for the SYMBOL.
+When called interactively, prompts the user for a symbol."
+ (interactive
+ (list (read-string "Symbol: " (clang-include-fixer--symbol-at-point))))
+ (clang-include-fixer--start #'clang-include-fixer--add-header
+ (format "-query-symbol=%s" symbol)))
+
+(defun clang-include-fixer--start (callback &rest args)
+ "Asynchronously start clang-include-fixer with parameters ARGS.
+The current file name is passed after ARGS as last argument. If
+the call was successful the returned result is stored in a
+temporary buffer, and CALLBACK is called with the temporary
+buffer as only argument."
+ (unless buffer-file-name
+ (user-error "clang-include-fixer works only in buffers that visit a file"))
+ (let ((process (if (fboundp 'make-process)
+ ;; Prefer using ‘make-process’ if available, because
+ ;; ‘start-process’ doesn’t allow us to separate the
+ ;; standard error from the output.
+ (clang-include-fixer--make-process callback args)
+ (clang-include-fixer--start-process callback args))))
+ (save-restriction
+ (widen)
+ (process-send-region process (point-min) (point-max)))
+ (process-send-eof process))
+ nil)
+
+(defun clang-include-fixer--make-process (callback args)
+ "Start a new clang-incude-fixer process using `make-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (stderr (generate-new-buffer "*clang-include-fixer errors*")))
+ (make-process :name "clang-include-fixer"
+ :buffer stdout
+ :command (clang-include-fixer--command args)
+ :coding 'utf-8-unix
+ :noquery t
+ :connection-type 'pipe
+ :sentinel (clang-include-fixer--sentinel stdin stdout stderr
+ callback)
+ :stderr stderr)))
+
+(defun clang-include-fixer--start-process (callback args)
+ "Start a new clang-incude-fixer process using `start-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted. ARGS is a list of additional command line
+arguments. Return the new process object."
+ (let* ((stdin (current-buffer))
+ (stdout (generate-new-buffer "*clang-include-fixer output*"))
+ (process-connection-type nil)
+ (process (apply #'start-process "clang-include-fixer" stdout
+ (clang-include-fixer--command args))))
+ (set-process-coding-system process 'utf-8-unix 'utf-8-unix)
+ (set-process-query-on-exit-flag process nil)
+ (set-process-sentinel process
+ (clang-include-fixer--sentinel stdin stdout nil
+ callback))
+ process))
+
+(defun clang-include-fixer--command (args)
+ "Return the clang-include-fixer command line.
+Returns a list; the first element is the binary to
+execute (`clang-include-fixer-executable'), and the remaining
+elements are the command line arguments. Adds proper arguments
+for `clang-include-fixer-input-format' and
+`clang-include-fixer-init-string'. Appends the current buffer's
+file name; prepends ARGS directly in front of it."
+ (cl-check-type args list)
+ `(,clang-include-fixer-executable
+ ,(format "-db=%s" clang-include-fixer-input-format)
+ ,(format "-input=%s" clang-include-fixer-init-string)
+ "-stdin"
+ ,@args
+ ,(buffer-file-name)))
+
+(defun clang-include-fixer--sentinel (stdin stdout stderr callback)
+ "Return a process sentinel for clang-include-fixer processes.
+STDIN, STDOUT, and STDERR are buffers for the standard streams;
+only STDERR may be nil. CALLBACK is called in the case of
+success; it is called with a single argument, STDOUT. On
+failure, a buffer containing the error output is displayed."
+ (cl-check-type stdin buffer-live)
+ (cl-check-type stdout buffer-live)
+ (cl-check-type stderr (or null buffer-live))
+ (cl-check-type callback function)
+ (lambda (process event)
+ (cl-check-type process process)
+ (cl-check-type event string)
+ (unwind-protect
+ (if (string-equal event "finished\n")
+ (progn
+ (when stderr (kill-buffer stderr))
+ (with-current-buffer stdin
+ (funcall callback stdout))
+ (kill-buffer stdout))
+ (when stderr (kill-buffer stdout))
+ (message "clang-include-fixer failed")
+ (with-current-buffer (or stderr stdout)
+ (insert "\nProcess " (process-name process)
+ ?\s event))
+ (display-buffer (or stderr stdout))))
+ nil))
+
+(defun clang-include-fixer--replace-buffer (stdout)
+ "Replace current buffer by content of STDOUT."
+ (cl-check-type stdout buffer-live)
+ (barf-if-buffer-read-only)
+ (cond ((fboundp 'replace-buffer-contents) (replace-buffer-contents stdout))
+ ((clang-include-fixer--insert-line stdout (current-buffer)))
+ (t (erase-buffer) (insert-buffer-substring stdout)))
+ (message "Fix applied")
+ nil)
+
+(defun clang-include-fixer--insert-line (from to)
+ "Insert a single missing line from the buffer FROM into TO.
+FROM and TO must be buffers. If the contents of FROM and TO are
+equal, do nothing and return non-nil. If FROM contains a single
+line missing from TO, insert that line into TO so that the buffer
+contents are equal and return non-nil. Otherwise, do nothing and
+return nil. Buffer restrictions are ignored."
+ (cl-check-type from buffer-live)
+ (cl-check-type to buffer-live)
+ (with-current-buffer from
+ (save-excursion
+ (save-restriction
+ (widen)
+ (with-current-buffer to
+ (save-excursion
+ (save-restriction
+ (widen)
+ ;; Search for the first buffer difference.
+ (let ((chars (abs (compare-buffer-substrings to nil nil from nil nil))))
+ (if (zerop chars)
+ ;; Buffer contents are equal, nothing to do.
+ t
+ (goto-char chars)
+ ;; We might have ended up in the middle of a line if the
+ ;; current line partially matches. In this case we would
+ ;; have to insert more than a line. Move to the beginning of
+ ;; the line to avoid this situation.
+ (beginning-of-line)
+ (with-current-buffer from
+ (goto-char chars)
+ (beginning-of-line)
+ (let ((from-begin (point))
+ (from-end (progn (forward-line) (point)))
+ (to-point (with-current-buffer to (point))))
+ ;; Search for another buffer difference after the line in
+ ;; question. If there is none, we can proceed.
+ (when (zerop (compare-buffer-substrings from from-end nil
+ to to-point nil))
+ (with-current-buffer to
+ (insert-buffer-substring from from-begin from-end))
+ t))))))))))))
+
+(defun clang-include-fixer--add-header (stdout)
+ "Analyse the result of include-fixer stored in STDOUT.
+Add a missing header if there is any. If there are multiple
+possible headers the user can select one of them to be included.
+Temporarily highlight the affected symbols. Asynchronously call
+clang-include-fixer to insert the selected header."
+ (cl-check-type stdout buffer-live)
+ (let ((context (clang-include-fixer--parse-json stdout)))
+ (let-alist context
+ (cond
+ ((null .QuerySymbolInfos)
+ (message "The file is fine, no need to add a header."))
+ ((null .HeaderInfos)
+ (message "Couldn't find header for '%s'"
+ (let-alist (car .QuerySymbolInfos) .RawIdentifier)))
+ (t
+ ;; Users may C-g in prompts, make sure the process sentinel
+ ;; behaves correctly.
+ (with-local-quit
+ ;; Replace the HeaderInfos list by a single header selected by
+ ;; the user.
+ (clang-include-fixer--select-header context)
+ ;; Call clang-include-fixer again to insert the selected header.
+ (clang-include-fixer--start
+ (let ((old-tick (buffer-chars-modified-tick)))
+ (lambda (stdout)
+ (when (/= old-tick (buffer-chars-modified-tick))
+ ;; Replacing the buffer now would undo the user’s changes.
+ (user-error (concat "The buffer has been changed "
+ "before the header could be inserted")))
+ (clang-include-fixer--replace-buffer stdout)
+ (let-alist context
+ (let-alist (car .HeaderInfos)
+ (with-local-quit
+ (run-hook-with-args 'clang-include-fixer-add-include-hook
+ (substring .Header 1 -1)
+ (string= (substring .Header 0 1) "<")))))))
+ (format "-insert-header=%s"
+ (clang-include-fixer--encode-json context))))))))
+ nil)
+
+(defun clang-include-fixer--select-header (context)
+ "Prompt the user for a header if necessary.
+CONTEXT must be a clang-include-fixer context object in
+association list format. If it contains more than one HeaderInfo
+element, prompt the user to select one of the headers. CONTEXT
+is modified to include only the selected element."
+ (cl-check-type context cons)
+ (let-alist context
+ (if (cdr .HeaderInfos)
+ (clang-include-fixer--prompt-for-header context)
+ (message "Only one include is missing: %s"
+ (let-alist (car .HeaderInfos) .Header))))
+ nil)
+
+(defvar clang-include-fixer--history nil
+ "History for `clang-include-fixer--prompt-for-header'.")
+
+(defun clang-include-fixer--prompt-for-header (context)
+ "Prompt the user for a single header.
+The choices are taken from the HeaderInfo elements in CONTEXT.
+They are replaced by the single element selected by the user."
+ (let-alist context
+ (let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos))
+ ;; Add temporary highlighting so that the user knows which
+ ;; symbols the current session is about.
+ (overlays (remove nil
+ (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos))))
+ (unwind-protect
+ (save-excursion
+ ;; While prompting, go to the closest overlay so that the user sees
+ ;; some context.
+ (when overlays
+ (goto-char (clang-include-fixer--closest-overlay overlays)))
+ (cl-flet ((header (info) (let-alist info .Header)))
+ ;; The header-infos is already sorted by include-fixer.
+ (let* ((header (completing-read
+ (clang-include-fixer--format-message
+ "Select include for '%s': " symbol)
+ (mapcar #'header .HeaderInfos)
+ nil :require-match nil
+ 'clang-include-fixer--history))
+ (info (cl-find header .HeaderInfos :key #'header :test #'string=)))
+ (cl-assert info)
+ (setcar .HeaderInfos info)
+ (setcdr .HeaderInfos nil))))
+ (mapc #'delete-overlay overlays)))))
+
+(defun clang-include-fixer--symbol-name (symbol-infos)
+ "Return the unique symbol name in SYMBOL-INFOS.
+Raise a signal if the symbol name is not unique."
+ (let ((symbols (delete-dups (mapcar (lambda (info)
+ (let-alist info .RawIdentifier))
+ symbol-infos))))
+ (when (cdr symbols)
+ (error "Multiple symbols %s returned" symbols))
+ (car symbols)))
+
+(defun clang-include-fixer--highlight (symbol-info)
+ "Add an overlay to highlight SYMBOL-INFO, if it points to a non-empty range.
+Return the overlay object, or nil."
+ (let-alist symbol-info
+ (unless (zerop .Range.Length)
+ (let ((overlay (make-overlay
+ (clang-include-fixer--filepos-to-bufferpos
+ .Range.Offset 'approximate)
+ (clang-include-fixer--filepos-to-bufferpos
+ (+ .Range.Offset .Range.Length) 'approximate))))
+ (overlay-put overlay 'face 'clang-include-fixer-highlight)
+ overlay))))
+
+(defun clang-include-fixer--closest-overlay (overlays)
+ "Return the start of the overlay in OVERLAYS that is closest to point."
+ (cl-check-type overlays cons)
+ (let ((point (point))
+ acc)
+ (dolist (overlay overlays acc)
+ (let ((start (overlay-start overlay)))
+ (when (or (null acc) (< (abs (- point start)) (abs (- point acc))))
+ (setq acc start))))))
+
+(defun clang-include-fixer--parse-json (buffer)
+ "Parse a JSON response from clang-include-fixer in BUFFER.
+Return the JSON object as an association list."
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (let ((json-object-type 'alist)
+ (json-array-type 'list)
+ (json-key-type 'symbol)
+ (json-false :json-false)
+ (json-null nil)
+ (json-pre-element-read-function nil)
+ (json-post-element-read-function nil))
+ (json-read)))))
+
+(defun clang-include-fixer--encode-json (object)
+ "Return the JSON representation of OBJECT as a string."
+ (let ((json-encoding-separator ",")
+ (json-encoding-default-indentation " ")
+ (json-encoding-pretty-print nil)
+ (json-encoding-lisp-style-closings nil)
+ (json-encoding-object-sort-predicate nil))
+ (json-encode object)))
+
+(defun clang-include-fixer--symbol-at-point ()
+ "Return the qualified symbol at point.
+If there is no symbol at point, return nil."
+ ;; Let ‘bounds-of-thing-at-point’ to do the hard work and deal with edge
+ ;; cases.
+ (let ((bounds (bounds-of-thing-at-point 'symbol)))
+ (when bounds
+ (let ((beg (car bounds))
+ (end (cdr bounds)))
+ (save-excursion
+ ;; Extend the symbol range to the left. Skip over namespace
+ ;; delimiters and parent namespace names.
+ (goto-char beg)
+ (while (and (clang-include-fixer--skip-double-colon-backward)
+ (skip-syntax-backward "w_")))
+ ;; Skip over one more namespace delimiter, for absolute names.
+ (clang-include-fixer--skip-double-colon-backward)
+ (setq beg (point))
+ ;; Extend the symbol range to the right. Skip over namespace
+ ;; delimiters and child namespace names.
+ (goto-char end)
+ (while (and (clang-include-fixer--skip-double-colon-forward)
+ (skip-syntax-forward "w_")))
+ (setq end (point)))
+ (buffer-substring-no-properties beg end)))))
+
+(defun clang-include-fixer--skip-double-colon-forward ()
+ "Skip a double colon.
+When the next two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((end (+ (point) 2)))
+ (when (and (<= end (point-max))
+ (string-equal (buffer-substring-no-properties (point) end) "::"))
+ (goto-char end)
+ t)))
+
+(defun clang-include-fixer--skip-double-colon-backward ()
+ "Skip a double colon.
+When the previous two characters are '::', skip them and return
+non-nil. Otherwise return nil."
+ (let ((beg (- (point) 2)))
+ (when (and (>= beg (point-min))
+ (string-equal (buffer-substring-no-properties beg (point)) "::"))
+ (goto-char beg)
+ t)))
+
+;; ‘filepos-to-bufferpos’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--filepos-to-bufferpos
+ (if (fboundp 'filepos-to-bufferpos)
+ 'filepos-to-bufferpos
+ (lambda (byte &optional _quality _coding-system)
+ (byte-to-position (1+ byte)))))
+
+;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--format-message
+ (if (fboundp 'format-message) 'format-message 'format))
+
+(provide 'clang-include-fixer)
+;;; clang-include-fixer.el ends here
--- /dev/null
+# This file is a minimal clang-include-fixer vim-integration. To install:
+# - Change 'binary' if clang-include-fixer is not on the path (see below).
+# - Add to your .vimrc:
+#
+# noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py<cr>
+#
+# This enables clang-include-fixer for NORMAL and VISUAL mode. Change "<leader>cf"
+# to another binding if you need clang-include-fixer on a different key.
+#
+# To set up clang-include-fixer, see http://clang.llvm.org/extra/include-fixer.html
+#
+# With this integration you can press the bound key and clang-include-fixer will
+# be run on the current buffer.
+#
+# It operates on the current, potentially unsaved buffer and does not create
+# or save any files. To revert a fix, just undo.
+
+import argparse
+import difflib
+import json
+import re
+import subprocess
+import vim
+
+# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
+# on the path.
+# Change this to the full path if clang-include-fixer is not on the path.
+binary = 'clang-include-fixer'
+if vim.eval('exists("g:clang_include_fixer_path")') == "1":
+ binary = vim.eval('g:clang_include_fixer_path')
+
+maximum_suggested_headers = 3
+if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
+ maximum_suggested_headers = max(
+ 1,
+ vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
+
+increment_num = 5
+if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
+ increment_num = max(
+ 1,
+ vim.eval('g:clang_include_fixer_increment_num'))
+
+jump_to_include = False
+if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
+ jump_to_include = vim.eval('g:clang_include_fixer_jump_to_include') != "0"
+
+query_mode = False
+if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
+ query_mode = vim.eval('g:clang_include_fixer_query_mode') != "0"
+
+
+def GetUserSelection(message, headers, maximum_suggested_headers):
+ eval_message = message + '\n'
+ for idx, header in enumerate(headers[0:maximum_suggested_headers]):
+ eval_message += "({0}). {1}\n".format(idx + 1, header)
+ eval_message += "Enter (q) to quit;"
+ if maximum_suggested_headers < len(headers):
+ eval_message += " (m) to show {0} more candidates.".format(
+ min(increment_num, len(headers) - maximum_suggested_headers))
+
+ eval_message += "\nSelect (default 1): "
+ res = vim.eval("input('{0}')".format(eval_message))
+ if res == '':
+ # choose the top ranked header by default
+ idx = 1
+ elif res == 'q':
+ raise Exception(' Insertion cancelled...')
+ elif res == 'm':
+ return GetUserSelection(message,
+ headers, maximum_suggested_headers + increment_num)
+ else:
+ try:
+ idx = int(res)
+ if idx <= 0 or idx > len(headers):
+ raise Exception()
+ except Exception:
+ # Show a new prompt on invalid option instead of aborting so that users
+ # don't need to wait for another include-fixer run.
+ print >> sys.stderr, "Invalid option:", res
+ return GetUserSelection(message, headers, maximum_suggested_headers)
+ return headers[idx - 1]
+
+
+def execute(command, text):
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE)
+ return p.communicate(input=text)
+
+
+def InsertHeaderToVimBuffer(header, text):
+ command = [binary, "-stdin", "-insert-header=" + json.dumps(header),
+ vim.current.buffer.name]
+ stdout, stderr = execute(command, text)
+ if stderr:
+ raise Exception(stderr)
+ if stdout:
+ lines = stdout.splitlines()
+ sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
+ line_num = None
+ for op in reversed(sequence.get_opcodes()):
+ if op[0] != 'equal':
+ vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+ if op[0] == 'insert':
+ # line_num in vim is 1-based.
+ line_num = op[1] + 1
+
+ if jump_to_include and line_num:
+ vim.current.window.cursor = (line_num, 0)
+
+
+# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
+# our use case very well, we re-implement our own one.
+def get_symbol_under_cursor():
+ line = vim.eval("line(\".\")")
+ # column number in vim is 1-based.
+ col = int(vim.eval("col(\".\")")) - 1
+ line_text = vim.eval("getline({0})".format(line))
+ if len(line_text) == 0: return ""
+ symbol_pos_begin = col
+ p = re.compile('[a-zA-Z0-9:_]')
+ while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
+ symbol_pos_begin -= 1
+
+ symbol_pos_end = col
+ while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
+ symbol_pos_end += 1
+ return line_text[symbol_pos_begin+1:symbol_pos_end]
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Vim integration for clang-include-fixer')
+ parser.add_argument('-db', default='yaml',
+ help='clang-include-fixer input format.')
+ parser.add_argument('-input', default='',
+ help='String to initialize the database.')
+ # Don't throw exception when parsing unknown arguements to make the script
+ # work in neovim.
+ # Neovim (at least v0.2.1) somehow mangles the sys.argv in a weird way: it
+ # will pass additional arguments (e.g. "-c script_host.py") to sys.argv,
+ # which makes the script fail.
+ args, _ = parser.parse_known_args()
+
+ # Get the current text.
+ buf = vim.current.buffer
+ text = '\n'.join(buf)
+
+ if query_mode:
+ symbol = get_symbol_under_cursor()
+ if len(symbol) == 0:
+ print "Skip querying empty symbol."
+ return
+ command = [binary, "-stdin", "-query-symbol="+get_symbol_under_cursor(),
+ "-db=" + args.db, "-input=" + args.input,
+ vim.current.buffer.name]
+ else:
+ # Run command to get all headers.
+ command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
+ "-input=" + args.input, vim.current.buffer.name]
+ stdout, stderr = execute(command, text)
+ if stderr:
+ print >> sys.stderr, "Error while running clang-include-fixer: " + stderr
+ return
+
+ include_fixer_context = json.loads(stdout)
+ query_symbol_infos = include_fixer_context["QuerySymbolInfos"]
+ if not query_symbol_infos:
+ print "The file is fine, no need to add a header."
+ return
+ symbol = query_symbol_infos[0]["RawIdentifier"]
+ # The header_infos is already sorted by include-fixer.
+ header_infos = include_fixer_context["HeaderInfos"]
+ # Deduplicate headers while keeping the order, so that the same header would
+ # not be suggested twice.
+ unique_headers = []
+ seen = set()
+ for header_info in header_infos:
+ header = header_info["Header"]
+ if header not in seen:
+ seen.add(header)
+ unique_headers.append(header)
+
+ if not unique_headers:
+ print "Couldn't find a header for {0}.".format(symbol)
+ return
+
+ try:
+ selected = unique_headers[0]
+ 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
+ return
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_tool(modularize
+ Modularize.cpp
+ ModuleAssistant.cpp
+ ModularizeUtilities.cpp
+ CoverageChecker.cpp
+ PreprocessorTracker.cpp
+ )
+
+target_link_libraries(modularize
+ clangAST
+ clangBasic
+ clangDriver
+ clangFrontend
+ clangLex
+ clangTooling
+ )
+
+install(TARGETS modularize
+ RUNTIME DESTINATION bin
+ COMPONENT clang-extras)
--- /dev/null
+//===--- extra/module-map-checker/CoverageChecker.cpp -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a class that validates a module map by checking that
+// all headers in the corresponding directories are accounted for.
+//
+// This class uses a previously loaded module map object.
+// Starting at the module map file directory, or just the include
+// paths, if specified, it will collect the names of all the files it
+// considers headers (no extension, .h, or .inc--if you need more, modify the
+// ModularizeUtilities::isHeader function).
+// It then compares the headers against those referenced
+// in the module map, either explicitly named, or implicitly named via an
+// umbrella directory or umbrella file, as parsed by the ModuleMap object.
+// If headers are found which are not referenced or covered by an umbrella
+// directory or file, warning messages will be produced, and the doChecks
+// function will return an error code of 1. Other errors result in an error
+// code of 2. If no problems are found, an error code of 0 is returned.
+//
+// Note that in the case of umbrella headers, this tool invokes the compiler
+// to preprocess the file, and uses a callback to collect the header files
+// included by the umbrella header or any of its nested includes. If any
+// front end options are needed for these compiler invocations, these are
+// to be passed in via the CommandLine parameter.
+//
+// Warning message have the form:
+//
+// warning: module.modulemap does not account for file: Level3A.h
+//
+// Note that for the case of the module map referencing a file that does
+// not exist, the module map parser in Clang will (at the time of this
+// writing) display an error message.
+//
+// Potential problems with this program:
+//
+// 1. Might need a better header matching mechanism, or extensions to the
+// canonical file format used.
+//
+// 2. It might need to support additional header file extensions.
+//
+// Future directions:
+//
+// 1. Add an option to fix the problems found, writing a new module map.
+// Include an extra option to add unaccounted-for headers as excluded.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ModularizeUtilities.h"
+#include "clang/AST/ASTConsumer.h"
+#include "CoverageChecker.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace Modularize;
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+namespace cl = llvm::cl;
+namespace sys = llvm::sys;
+
+// Preprocessor callbacks.
+// We basically just collect include files.
+class CoverageCheckerCallbacks : public PPCallbacks {
+public:
+ CoverageCheckerCallbacks(CoverageChecker &Checker) : Checker(Checker) {}
+ ~CoverageCheckerCallbacks() override {}
+
+ // Include directive callback.
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FilenameRange, const FileEntry *File,
+ StringRef SearchPath, StringRef RelativePath,
+ const Module *Imported) override {
+ Checker.collectUmbrellaHeaderHeader(File->getName());
+ }
+
+private:
+ CoverageChecker &Checker;
+};
+
+// Frontend action stuff:
+
+// Consumer is responsible for setting up the callbacks.
+class CoverageCheckerConsumer : public ASTConsumer {
+public:
+ CoverageCheckerConsumer(CoverageChecker &Checker, Preprocessor &PP) {
+ // PP takes ownership.
+ PP.addPPCallbacks(llvm::make_unique<CoverageCheckerCallbacks>(Checker));
+ }
+};
+
+class CoverageCheckerAction : public SyntaxOnlyAction {
+public:
+ CoverageCheckerAction(CoverageChecker &Checker) : Checker(Checker) {}
+
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ return llvm::make_unique<CoverageCheckerConsumer>(Checker,
+ CI.getPreprocessor());
+ }
+
+private:
+ CoverageChecker &Checker;
+};
+
+class CoverageCheckerFrontendActionFactory : public FrontendActionFactory {
+public:
+ CoverageCheckerFrontendActionFactory(CoverageChecker &Checker)
+ : Checker(Checker) {}
+
+ CoverageCheckerAction *create() override {
+ return new CoverageCheckerAction(Checker);
+ }
+
+private:
+ CoverageChecker &Checker;
+};
+
+// CoverageChecker class implementation.
+
+// Constructor.
+CoverageChecker::CoverageChecker(StringRef ModuleMapPath,
+ std::vector<std::string> &IncludePaths,
+ ArrayRef<std::string> CommandLine,
+ clang::ModuleMap *ModuleMap)
+ : ModuleMapPath(ModuleMapPath), IncludePaths(IncludePaths),
+ CommandLine(CommandLine),
+ ModMap(ModuleMap) {}
+
+// Create instance of CoverageChecker, to simplify setting up
+// subordinate objects.
+std::unique_ptr<CoverageChecker> CoverageChecker::createCoverageChecker(
+ StringRef ModuleMapPath, std::vector<std::string> &IncludePaths,
+ ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap) {
+
+ return llvm::make_unique<CoverageChecker>(ModuleMapPath, IncludePaths,
+ CommandLine, ModuleMap);
+}
+
+// Do checks.
+// Starting from the directory of the module.modulemap file,
+// Find all header files, optionally looking only at files
+// covered by the include path options, and compare against
+// the headers referenced by the module.modulemap file.
+// Display warnings for unaccounted-for header files.
+// Returns error_code of 0 if there were no errors or warnings, 1 if there
+// were warnings, 2 if any other problem, such as if a bad
+// module map path argument was specified.
+std::error_code CoverageChecker::doChecks() {
+ std::error_code returnValue;
+
+ // Collect the headers referenced in the modules.
+ collectModuleHeaders();
+
+ // Collect the file system headers.
+ if (!collectFileSystemHeaders())
+ return std::error_code(2, std::generic_category());
+
+ // Do the checks. These save the problematic file names.
+ findUnaccountedForHeaders();
+
+ // Check for warnings.
+ if (!UnaccountedForHeaders.empty())
+ returnValue = std::error_code(1, std::generic_category());
+
+ return returnValue;
+}
+
+// The following functions are called by doChecks.
+
+// Collect module headers.
+// Walks the modules and collects referenced headers into
+// ModuleMapHeadersSet.
+void CoverageChecker::collectModuleHeaders() {
+ for (ModuleMap::module_iterator I = ModMap->module_begin(),
+ E = ModMap->module_end();
+ I != E; ++I) {
+ collectModuleHeaders(*I->second);
+ }
+}
+
+// Collect referenced headers from one module.
+// Collects the headers referenced in the given module into
+// ModuleMapHeadersSet.
+// FIXME: Doesn't collect files from umbrella header.
+bool CoverageChecker::collectModuleHeaders(const Module &Mod) {
+
+ if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
+ // Collect umbrella header.
+ ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(
+ UmbrellaHeader->getName()));
+ // Preprocess umbrella header and collect the headers it references.
+ if (!collectUmbrellaHeaderHeaders(UmbrellaHeader->getName()))
+ return false;
+ }
+ else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
+ // Collect headers in umbrella directory.
+ if (!collectUmbrellaHeaders(UmbrellaDir->getName()))
+ return false;
+ }
+
+ for (auto &HeaderKind : Mod.Headers)
+ for (auto &Header : HeaderKind)
+ ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(
+ Header.Entry->getName()));
+
+ for (auto MI = Mod.submodule_begin(), MIEnd = Mod.submodule_end();
+ MI != MIEnd; ++MI)
+ collectModuleHeaders(**MI);
+
+ return true;
+}
+
+// Collect headers from an umbrella directory.
+bool CoverageChecker::collectUmbrellaHeaders(StringRef UmbrellaDirName) {
+ // Initialize directory name.
+ SmallString<256> Directory(ModuleMapDirectory);
+ if (UmbrellaDirName.size())
+ sys::path::append(Directory, UmbrellaDirName);
+ if (Directory.size() == 0)
+ Directory = ".";
+ // Walk the directory.
+ std::error_code EC;
+ sys::fs::file_status Status;
+ for (sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
+ I.increment(EC)) {
+ if (EC)
+ return false;
+ std::string File(I->path());
+ I->status(Status);
+ sys::fs::file_type Type = Status.type();
+ // If the file is a directory, ignore the name and recurse.
+ if (Type == sys::fs::file_type::directory_file) {
+ if (!collectUmbrellaHeaders(File))
+ return false;
+ continue;
+ }
+ // If the file does not have a common header extension, ignore it.
+ if (!ModularizeUtilities::isHeader(File))
+ continue;
+ // Save header name.
+ ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(File));
+ }
+ return true;
+}
+
+// Collect headers rferenced from an umbrella file.
+bool
+CoverageChecker::collectUmbrellaHeaderHeaders(StringRef UmbrellaHeaderName) {
+
+ SmallString<256> PathBuf(ModuleMapDirectory);
+
+ // If directory is empty, it's the current directory.
+ if (ModuleMapDirectory.length() == 0)
+ sys::fs::current_path(PathBuf);
+
+ // Create the compilation database.
+ std::unique_ptr<CompilationDatabase> Compilations;
+ Compilations.reset(new FixedCompilationDatabase(Twine(PathBuf), CommandLine));
+
+ std::vector<std::string> HeaderPath;
+ HeaderPath.push_back(UmbrellaHeaderName);
+
+ // Create the tool and run the compilation.
+ ClangTool Tool(*Compilations, HeaderPath);
+ int HadErrors = Tool.run(new CoverageCheckerFrontendActionFactory(*this));
+
+ // If we had errors, exit early.
+ return !HadErrors;
+}
+
+// Called from CoverageCheckerCallbacks to track a header included
+// from an umbrella header.
+void CoverageChecker::collectUmbrellaHeaderHeader(StringRef HeaderName) {
+
+ SmallString<256> PathBuf(ModuleMapDirectory);
+ // If directory is empty, it's the current directory.
+ if (ModuleMapDirectory.length() == 0)
+ sys::fs::current_path(PathBuf);
+ // HeaderName will have an absolute path, so if it's the module map
+ // directory, we remove it, also skipping trailing separator.
+ if (HeaderName.startswith(PathBuf))
+ HeaderName = HeaderName.substr(PathBuf.size() + 1);
+ // Save header name.
+ ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(HeaderName));
+}
+
+// Collect file system header files.
+// This function scans the file system for header files,
+// starting at the directory of the module.modulemap file,
+// optionally filtering out all but the files covered by
+// the include path options.
+// Returns true if no errors.
+bool CoverageChecker::collectFileSystemHeaders() {
+
+ // Get directory containing the module.modulemap file.
+ // Might be relative to current directory, absolute, or empty.
+ ModuleMapDirectory = ModularizeUtilities::getDirectoryFromPath(ModuleMapPath);
+
+ // If no include paths specified, we do the whole tree starting
+ // at the module.modulemap directory.
+ if (IncludePaths.size() == 0) {
+ if (!collectFileSystemHeaders(StringRef("")))
+ return false;
+ }
+ else {
+ // Otherwise we only look at the sub-trees specified by the
+ // include paths.
+ for (std::vector<std::string>::const_iterator I = IncludePaths.begin(),
+ E = IncludePaths.end();
+ I != E; ++I) {
+ if (!collectFileSystemHeaders(*I))
+ return false;
+ }
+ }
+
+ // Sort it, because different file systems might order the file differently.
+ std::sort(FileSystemHeaders.begin(), FileSystemHeaders.end());
+
+ return true;
+}
+
+// Collect file system header files from the given path.
+// This function scans the file system for header files,
+// starting at the given directory, which is assumed to be
+// relative to the directory of the module.modulemap file.
+// \returns True if no errors.
+bool CoverageChecker::collectFileSystemHeaders(StringRef IncludePath) {
+
+ // Initialize directory name.
+ SmallString<256> Directory(ModuleMapDirectory);
+ if (IncludePath.size())
+ sys::path::append(Directory, IncludePath);
+ if (Directory.size() == 0)
+ Directory = ".";
+ if (IncludePath.startswith("/") || IncludePath.startswith("\\") ||
+ ((IncludePath.size() >= 2) && (IncludePath[1] == ':'))) {
+ llvm::errs() << "error: Include path \"" << IncludePath
+ << "\" is not relative to the module map file.\n";
+ return false;
+ }
+
+ // Recursively walk the directory tree.
+ std::error_code EC;
+ sys::fs::file_status Status;
+ int Count = 0;
+ for (sys::fs::recursive_directory_iterator I(Directory.str(), EC), E; I != E;
+ I.increment(EC)) {
+ if (EC)
+ return false;
+ //std::string file(I->path());
+ StringRef file(I->path());
+ I->status(Status);
+ sys::fs::file_type type = Status.type();
+ // If the file is a directory, ignore the name (but still recurses).
+ if (type == sys::fs::file_type::directory_file)
+ continue;
+ // Assume directories or files starting with '.' are private and not to
+ // be considered.
+ if ((file.find("\\.") != StringRef::npos) ||
+ (file.find("/.") != StringRef::npos))
+ continue;
+ // If the file does not have a common header extension, ignore it.
+ if (!ModularizeUtilities::isHeader(file))
+ continue;
+ // Save header name.
+ FileSystemHeaders.push_back(ModularizeUtilities::getCanonicalPath(file));
+ Count++;
+ }
+ if (Count == 0) {
+ llvm::errs() << "warning: No headers found in include path: \""
+ << IncludePath << "\"\n";
+ }
+ return true;
+}
+
+// Find headers unaccounted-for in module map.
+// This function compares the list of collected header files
+// against those referenced in the module map. Display
+// warnings for unaccounted-for header files.
+// Save unaccounted-for file list for possible.
+// fixing action.
+// FIXME: There probably needs to be some canonalization
+// of file names so that header path can be correctly
+// matched. Also, a map could be used for the headers
+// referenced in the module, but
+void CoverageChecker::findUnaccountedForHeaders() {
+ // Walk over file system headers.
+ for (std::vector<std::string>::const_iterator I = FileSystemHeaders.begin(),
+ E = FileSystemHeaders.end();
+ I != E; ++I) {
+ // Look for header in module map.
+ if (ModuleMapHeadersSet.insert(*I).second) {
+ UnaccountedForHeaders.push_back(*I);
+ llvm::errs() << "warning: " << ModuleMapPath
+ << " does not account for file: " << *I << "\n";
+ }
+ }
+}
--- /dev/null
+//===-- CoverageChecker.h - Module map coverage checker -*- C++ -*-------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Definitions for CoverageChecker.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef COVERAGECHECKER_H
+#define COVERAGECHECKER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/ModuleMap.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Host.h"
+#include <string>
+#include <vector>
+
+namespace Modularize {
+
+/// Module map checker class.
+/// This is the heart of the checker.
+/// The doChecks function does the main work.
+/// The data members store the options and internally collected data.
+class CoverageChecker {
+ // Checker arguments.
+
+ /// The module.modulemap file path. Can be relative or absolute.
+ llvm::StringRef ModuleMapPath;
+ /// The include paths to check for files.
+ /// (Note that other directories above these paths are ignored.
+ /// To expect all files to be accounted for from the module.modulemap
+ /// file directory on down, leave this empty.)
+ std::vector<std::string> IncludePaths;
+ /// The remaining arguments, to be passed to the front end.
+ llvm::ArrayRef<std::string> CommandLine;
+ /// The module map.
+ clang::ModuleMap *ModMap;
+
+ // Internal data.
+
+ /// Directory containing the module map.
+ /// Might be relative to the current directory, or absolute.
+ std::string ModuleMapDirectory;
+ /// Set of all the headers found in the module map.
+ llvm::StringSet<llvm::MallocAllocator> ModuleMapHeadersSet;
+ /// All the headers found in the file system starting at the
+ /// module map, or the union of those from the include paths.
+ std::vector<std::string> FileSystemHeaders;
+ /// Headers found in file system, but not in module map.
+ std::vector<std::string> UnaccountedForHeaders;
+
+public:
+ /// Constructor.
+ /// You can use the static createCoverageChecker to create an instance
+ /// of this object.
+ /// \param ModuleMapPath The module.modulemap file path.
+ /// Can be relative or absolute.
+ /// \param IncludePaths The include paths to check for files.
+ /// (Note that other directories above these paths are ignored.
+ /// To expect all files to be accounted for from the module.modulemap
+ /// file directory on down, leave this empty.)
+ /// \param CommandLine Compile command line arguments.
+ /// \param ModuleMap The module map to check.
+ CoverageChecker(llvm::StringRef ModuleMapPath,
+ std::vector<std::string> &IncludePaths,
+ llvm::ArrayRef<std::string> CommandLine,
+ clang::ModuleMap *ModuleMap);
+
+ /// Create instance of CoverageChecker.
+ /// \param ModuleMapPath The module.modulemap file path.
+ /// Can be relative or absolute.
+ /// \param IncludePaths The include paths to check for files.
+ /// (Note that other directories above these paths are ignored.
+ /// To expect all files to be accounted for from the module.modulemap
+ /// file directory on down, leave this empty.)
+ /// \param CommandLine Compile command line arguments.
+ /// \param ModuleMap The module map to check.
+ /// \returns Initialized CoverageChecker object.
+ static std::unique_ptr<CoverageChecker> createCoverageChecker(
+ llvm::StringRef ModuleMapPath, std::vector<std::string> &IncludePaths,
+ llvm::ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap);
+
+ /// Do checks.
+ /// Starting from the directory of the module.modulemap file,
+ /// Find all header files, optionally looking only at files
+ /// covered by the include path options, and compare against
+ /// the headers referenced by the module.modulemap file.
+ /// Display warnings for unaccounted-for header files.
+ /// \returns 0 if there were no errors or warnings, 1 if there
+ /// were warnings, 2 if any other problem, such as a bad
+ /// module map path argument was specified.
+ std::error_code doChecks();
+
+ // The following functions are called by doChecks.
+
+ /// Collect module headers.
+ /// Walks the modules and collects referenced headers into
+ /// ModuleMapHeadersSet.
+ void collectModuleHeaders();
+
+ /// Collect referenced headers from one module.
+ /// Collects the headers referenced in the given module into
+ /// ModuleMapHeadersSet.
+ /// \param Mod The module reference.
+ /// \return True if no errors.
+ bool collectModuleHeaders(const clang::Module &Mod);
+
+ /// Collect headers from an umbrella directory.
+ /// \param UmbrellaDirName The umbrella directory name.
+ /// \return True if no errors.
+ bool collectUmbrellaHeaders(llvm::StringRef UmbrellaDirName);
+
+ /// Collect headers rferenced from an umbrella file.
+ /// \param UmbrellaHeaderName The umbrella file path.
+ /// \return True if no errors.
+ bool collectUmbrellaHeaderHeaders(llvm::StringRef UmbrellaHeaderName);
+
+ /// Called from CoverageCheckerCallbacks to track a header included
+ /// from an umbrella header.
+ /// \param HeaderName The header file path.
+ void collectUmbrellaHeaderHeader(llvm::StringRef HeaderName);
+
+ /// Collect file system header files.
+ /// This function scans the file system for header files,
+ /// starting at the directory of the module.modulemap file,
+ /// optionally filtering out all but the files covered by
+ /// the include path options.
+ /// \returns True if no errors.
+ bool collectFileSystemHeaders();
+
+ /// Collect file system header files from the given path.
+ /// This function scans the file system for header files,
+ /// starting at the given directory, which is assumed to be
+ /// relative to the directory of the module.modulemap file.
+ /// \returns True if no errors.
+ bool collectFileSystemHeaders(llvm::StringRef IncludePath);
+
+ /// Find headers unaccounted-for in module map.
+ /// This function compares the list of collected header files
+ /// against those referenced in the module map. Display
+ /// warnings for unaccounted-for header files.
+ /// Save unaccounted-for file list for possible.
+ /// fixing action.
+ void findUnaccountedForHeaders();
+};
+
+} // end namespace Modularize
+
+#endif // COVERAGECHECKER_H
--- /dev/null
+//===- extra/modularize/Modularize.cpp - Check modularized headers --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Introduction
+//
+// This file implements a tool that checks whether a set of headers provides
+// the consistent definitions required to use modules. It can also check an
+// existing module map for full coverage of the headers in a directory tree.
+//
+// For example, in examining headers, it detects whether the same entity
+// (say, a NULL macro or size_t typedef) is defined in multiple headers
+// or whether a header produces different definitions under
+// different circumstances. These conditions cause modules built from the
+// headers to behave poorly, and should be fixed before introducing a module
+// map.
+//
+// Modularize takes as input either one or more module maps (by default,
+// "module.modulemap") or one or more text files contatining lists of headers
+// to check.
+//
+// In the case of a module map, the module map must be well-formed in
+// terms of syntax. Modularize will extract the header file names
+// from the map. Only normal headers are checked, assuming headers
+// marked "private", "textual", or "exclude" are not to be checked
+// as a top-level include, assuming they either are included by
+// other headers which are checked, or they are not suitable for
+// modules.
+//
+// In the case of a file list, the list is a newline-separated list of headers
+// to check with respect to each other.
+// Lines beginning with '#' and empty lines are ignored.
+// Header file names followed by a colon and other space-separated
+// file names will include those extra files as dependencies.
+// The file names can be relative or full paths, but must be on the
+// same line.
+//
+// Modularize also accepts regular clang front-end arguments.
+//
+// Usage: modularize [(modularize options)]
+// [(include-files_list)|(module map)]+ [(front-end-options) ...]
+//
+// Options:
+// -prefix=(optional header path prefix)
+// Note that unless a "-prefix (header path)" option is specified,
+// non-absolute file paths in the header list file will be relative
+// to the header list file directory. Use -prefix to specify a
+// different directory.
+// -module-map-path=(module map)
+// Skip the checks, and instead act as a module.map generation
+// assistant, generating a module map file based on the header list.
+// An optional "-root-module=(rootName)" argument can specify a root
+// module to be created in the generated module.map file. Note that
+// you will likely need to edit this file to suit the needs of your
+// headers.
+// -problem-files-list=(problem files list file name)
+// For use only with module map assistant. Input list of files that
+// have problems with respect to modules. These will still be
+// included in the generated module map, but will be marked as
+// "excluded" headers.
+// -root-module=(root module name)
+// Specifies a root module to be created in the generated module.map
+// file.
+// -block-check-header-list-only
+// Only warn if #include directives are inside extern or namespace
+// blocks if the included header is in the header list.
+// -no-coverage-check
+// Don't do the coverage check.
+// -coverage-check-only
+// Only do the coverage check.
+// -display-file-lists
+// Display lists of good files (no compile errors), problem files,
+// and a combined list with problem files preceded by a '#'.
+// This can be used to quickly determine which files have problems.
+// The latter combined list might be useful in starting to modularize
+// a set of headers. You can start with a full list of headers,
+// use -display-file-lists option, and then use the combined list as
+// your intermediate list, uncommenting-out headers as you fix them.
+//
+// Note that by default, the modularize assumes .h files contain C++ source.
+// If your .h files in the file list contain another language, you should
+// append an appropriate -x option to your command line, i.e.: -x c
+//
+// Modularization Issue Checks
+//
+// In the process of checking headers for modularization issues, modularize
+// will do normal parsing, reporting normal errors and warnings,
+// but will also report special error messages like the following:
+//
+// error: '(symbol)' defined at multiple locations:
+// (file):(row):(column)
+// (file):(row):(column)
+//
+// error: header '(file)' has different contents depending on how it was
+// included
+//
+// The latter might be followed by messages like the following:
+//
+// note: '(symbol)' in (file) at (row):(column) not always provided
+//
+// Checks will also be performed for macro expansions, defined(macro)
+// expressions, and preprocessor conditional directives that evaluate
+// inconsistently, and can produce error messages like the following:
+//
+// (...)/SubHeader.h:11:5:
+// #if SYMBOL == 1
+// ^
+// error: Macro instance 'SYMBOL' has different values in this header,
+// depending on how it was included.
+// 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+// (...)/Header1.h
+// (...)/SubHeader.h
+// (...)/SubHeader.h:3:9:
+// #define SYMBOL 1
+// ^
+// Macro defined here.
+// 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+// (...)/Header2.h
+// (...)/SubHeader.h
+// (...)/SubHeader.h:7:9:
+// #define SYMBOL 2
+// ^
+// Macro defined here.
+//
+// Checks will also be performed for '#include' directives that are
+// nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
+// and can produce error message like the following:
+//
+// IncludeInExtern.h:2:3
+// #include "Empty.h"
+// ^
+// error: Include directive within extern "C" {}.
+// IncludeInExtern.h:1:1
+// extern "C" {
+// ^
+// The "extern "C" {}" block is here.
+//
+// See PreprocessorTracker.cpp for additional details.
+//
+// Module Map Coverage Check
+//
+// The coverage check uses the Clang ModuleMap class to read and parse the
+// module map file. Starting at the module map file directory, or just the
+// include paths, if specified, it will collect the names of all the files it
+// considers headers (no extension, .h, or .inc--if you need more, modify the
+// isHeader function). It then compares the headers against those referenced
+// in the module map, either explicitly named, or implicitly named via an
+// umbrella directory or umbrella file, as parsed by the ModuleMap object.
+// If headers are found which are not referenced or covered by an umbrella
+// directory or file, warning messages will be produced, and this program
+// will return an error code of 1. Other errors result in an error code of 2.
+// If no problems are found, an error code of 0 is returned.
+//
+// Note that in the case of umbrella headers, this tool invokes the compiler
+// to preprocess the file, and uses a callback to collect the header files
+// included by the umbrella header or any of its nested includes. If any
+// front end options are needed for these compiler invocations, these
+// can be included on the command line after the module map file argument.
+//
+// Warning message have the form:
+//
+// warning: module.modulemap does not account for file: Level3A.h
+//
+// Note that for the case of the module map referencing a file that does
+// not exist, the module map parser in Clang will (at the time of this
+// writing) display an error message.
+//
+// Module Map Assistant - Module Map Generation
+//
+// Modularize also has an option ("-module-map-path=module.modulemap") that will
+// skip the checks, and instead act as a module.modulemap generation assistant,
+// generating a module map file based on the header list. An optional
+// "-root-module=(rootName)" argument can specify a root module to be
+// created in the generated module.modulemap file. Note that you will likely
+// need to edit this file to suit the needs of your headers.
+//
+// An example command line for generating a module.modulemap file:
+//
+// modularize -module-map-path=module.modulemap -root-module=myroot \
+// headerlist.txt
+//
+// Note that if the headers in the header list have partial paths, sub-modules
+// will be created for the subdirectires involved, assuming that the
+// subdirectories contain headers to be grouped into a module, but still with
+// individual modules for the headers in the subdirectory.
+//
+// See the ModuleAssistant.cpp file comments for additional details about the
+// implementation of the assistant mode.
+//
+// Future directions:
+//
+// Basically, we want to add new checks for whatever we can check with respect
+// to checking headers for module'ability.
+//
+// Some ideas:
+//
+// 1. Omit duplicate "not always provided" messages
+//
+// 2. Add options to disable any of the checks, in case
+// there is some problem with them, or the messages get too verbose.
+//
+// 3. Try to figure out the preprocessor conditional directives that
+// contribute to problems and tie them to the inconsistent definitions.
+//
+// 4. There are some legitimate uses of preprocessor macros that
+// modularize will flag as errors, such as repeatedly #include'ing
+// a file and using interleaving defined/undefined macros
+// to change declarations in the included file. Is there a way
+// to address this? Maybe have modularize accept a list of macros
+// to ignore. Otherwise you can just exclude the file, after checking
+// for legitimate errors.
+//
+// 5. What else?
+//
+// General clean-up and refactoring:
+//
+// 1. The Location class seems to be something that we might
+// want to design to be applicable to a wider range of tools, and stick it
+// somewhere into Tooling/ in mainline
+//
+//===----------------------------------------------------------------------===//
+
+#include "Modularize.h"
+#include "ModularizeUtilities.h"
+#include "PreprocessorTracker.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+using namespace llvm;
+using namespace llvm::opt;
+using namespace Modularize;
+
+// Option to specify a file name for a list of header files to check.
+static cl::list<std::string>
+ ListFileNames(cl::Positional, cl::value_desc("list"),
+ cl::desc("<list of one or more header list files>"),
+ cl::CommaSeparated);
+
+// Collect all other arguments, which will be passed to the front end.
+static cl::list<std::string>
+ CC1Arguments(cl::ConsumeAfter,
+ cl::desc("<arguments to be passed to front end>..."));
+
+// Option to specify a prefix to be prepended to the header names.
+static cl::opt<std::string> HeaderPrefix(
+ "prefix", cl::init(""),
+ cl::desc(
+ "Prepend header file paths with this prefix."
+ " If not specified,"
+ " the files are considered to be relative to the header list file."));
+
+// Option for assistant mode, telling modularize to output a module map
+// based on the headers list, and where to put it.
+static cl::opt<std::string> ModuleMapPath(
+ "module-map-path", cl::init(""),
+ cl::desc("Turn on module map output and specify output path or file name."
+ " If no path is specified and if prefix option is specified,"
+ " use prefix for file path."));
+
+// Option to specify list of problem files for assistant.
+// This will cause assistant to exclude these files.
+static cl::opt<std::string> ProblemFilesList(
+ "problem-files-list", cl::init(""),
+ cl::desc(
+ "List of files with compilation or modularization problems for"
+ " assistant mode. This will be excluded."));
+
+// Option for assistant mode, telling modularize the name of the root module.
+static cl::opt<std::string>
+RootModule("root-module", cl::init(""),
+ cl::desc("Specify the name of the root module."));
+
+// Option for limiting the #include-inside-extern-or-namespace-block
+// check to only those headers explicitly listed in the header list.
+// This is a work-around for private includes that purposefully get
+// included inside blocks.
+static cl::opt<bool>
+BlockCheckHeaderListOnly("block-check-header-list-only", cl::init(false),
+cl::desc("Only warn if #include directives are inside extern or namespace"
+ " blocks if the included header is in the header list."));
+
+// Option for include paths for coverage check.
+static cl::list<std::string>
+IncludePaths("I", cl::desc("Include path for coverage check."),
+cl::ZeroOrMore, cl::value_desc("path"));
+
+// Option for disabling the coverage check.
+static cl::opt<bool>
+NoCoverageCheck("no-coverage-check", cl::init(false),
+cl::desc("Don't do the coverage check."));
+
+// Option for just doing the coverage check.
+static cl::opt<bool>
+CoverageCheckOnly("coverage-check-only", cl::init(false),
+cl::desc("Only do the coverage check."));
+
+// Option for displaying lists of good, bad, and mixed files.
+static cl::opt<bool>
+DisplayFileLists("display-file-lists", cl::init(false),
+cl::desc("Display lists of good files (no compile errors), problem files,"
+ " and a combined list with problem files preceded by a '#'."));
+
+// Save the program name for error messages.
+const char *Argv0;
+// Save the command line for comments.
+std::string CommandLine;
+
+// Helper function for finding the input file in an arguments list.
+static std::string findInputFile(const CommandLineArguments &CLArgs) {
+ std::unique_ptr<OptTable> Opts(createDriverOptTable());
+ const unsigned IncludedFlagsBitmask = options::CC1Option;
+ unsigned MissingArgIndex, MissingArgCount;
+ SmallVector<const char *, 256> Argv;
+ for (auto I = CLArgs.begin(), E = CLArgs.end(); I != E; ++I)
+ Argv.push_back(I->c_str());
+ InputArgList Args = Opts->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
+ IncludedFlagsBitmask);
+ std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
+ return ModularizeUtilities::getCanonicalPath(Inputs.back());
+}
+
+// This arguments adjuster inserts "-include (file)" arguments for header
+// dependencies. It also inserts a "-w" option and a "-x c++",
+// if no other "-x" option is present.
+static ArgumentsAdjuster
+getModularizeArgumentsAdjuster(DependencyMap &Dependencies) {
+ return [&Dependencies](const CommandLineArguments &Args,
+ StringRef /*unused*/) {
+ std::string InputFile = findInputFile(Args);
+ DependentsVector &FileDependents = Dependencies[InputFile];
+ CommandLineArguments NewArgs(Args);
+ if (int Count = FileDependents.size()) {
+ for (int Index = 0; Index < Count; ++Index) {
+ NewArgs.push_back("-include");
+ std::string File(std::string("\"") + FileDependents[Index] +
+ std::string("\""));
+ NewArgs.push_back(FileDependents[Index]);
+ }
+ }
+ // Ignore warnings. (Insert after "clang_tool" at beginning.)
+ NewArgs.insert(NewArgs.begin() + 1, "-w");
+ // Since we are compiling .h files, assume C++ unless given a -x option.
+ if (std::find(NewArgs.begin(), NewArgs.end(), "-x") == NewArgs.end()) {
+ NewArgs.insert(NewArgs.begin() + 2, "-x");
+ NewArgs.insert(NewArgs.begin() + 3, "c++");
+ }
+ return NewArgs;
+ };
+}
+
+// FIXME: The Location class seems to be something that we might
+// want to design to be applicable to a wider range of tools, and stick it
+// somewhere into Tooling/ in mainline
+struct Location {
+ const FileEntry *File;
+ unsigned Line, Column;
+
+ Location() : File(), Line(), Column() {}
+
+ Location(SourceManager &SM, SourceLocation Loc) : File(), Line(), Column() {
+ Loc = SM.getExpansionLoc(Loc);
+ if (Loc.isInvalid())
+ return;
+
+ std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
+ File = SM.getFileEntryForID(Decomposed.first);
+ if (!File)
+ return;
+
+ Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
+ Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
+ }
+
+ operator bool() const { return File != nullptr; }
+
+ friend bool operator==(const Location &X, const Location &Y) {
+ return X.File == Y.File && X.Line == Y.Line && X.Column == Y.Column;
+ }
+
+ friend bool operator!=(const Location &X, const Location &Y) {
+ return !(X == Y);
+ }
+
+ friend bool operator<(const Location &X, const Location &Y) {
+ if (X.File != Y.File)
+ return X.File < Y.File;
+ if (X.Line != Y.Line)
+ return X.Line < Y.Line;
+ return X.Column < Y.Column;
+ }
+ friend bool operator>(const Location &X, const Location &Y) { return Y < X; }
+ friend bool operator<=(const Location &X, const Location &Y) {
+ return !(Y < X);
+ }
+ friend bool operator>=(const Location &X, const Location &Y) {
+ return !(X < Y);
+ }
+};
+
+struct Entry {
+ enum EntryKind {
+ EK_Tag,
+ EK_Value,
+ EK_Macro,
+
+ EK_NumberOfKinds
+ } Kind;
+
+ Location Loc;
+
+ StringRef getKindName() { return getKindName(Kind); }
+ static StringRef getKindName(EntryKind kind);
+};
+
+// Return a string representing the given kind.
+StringRef Entry::getKindName(Entry::EntryKind kind) {
+ switch (kind) {
+ case EK_Tag:
+ return "tag";
+ case EK_Value:
+ return "value";
+ case EK_Macro:
+ return "macro";
+ case EK_NumberOfKinds:
+ break;
+ }
+ llvm_unreachable("invalid Entry kind");
+}
+
+struct HeaderEntry {
+ std::string Name;
+ Location Loc;
+
+ friend bool operator==(const HeaderEntry &X, const HeaderEntry &Y) {
+ return X.Loc == Y.Loc && X.Name == Y.Name;
+ }
+ friend bool operator!=(const HeaderEntry &X, const HeaderEntry &Y) {
+ return !(X == Y);
+ }
+ friend bool operator<(const HeaderEntry &X, const HeaderEntry &Y) {
+ return X.Loc < Y.Loc || (X.Loc == Y.Loc && X.Name < Y.Name);
+ }
+ friend bool operator>(const HeaderEntry &X, const HeaderEntry &Y) {
+ return Y < X;
+ }
+ friend bool operator<=(const HeaderEntry &X, const HeaderEntry &Y) {
+ return !(Y < X);
+ }
+ friend bool operator>=(const HeaderEntry &X, const HeaderEntry &Y) {
+ return !(X < Y);
+ }
+};
+
+typedef std::vector<HeaderEntry> HeaderContents;
+
+class EntityMap : public StringMap<SmallVector<Entry, 2> > {
+public:
+ DenseMap<const FileEntry *, HeaderContents> HeaderContentMismatches;
+
+ void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc) {
+ // Record this entity in its header.
+ HeaderEntry HE = { Name, Loc };
+ CurHeaderContents[Loc.File].push_back(HE);
+
+ // Check whether we've seen this entry before.
+ SmallVector<Entry, 2> &Entries = (*this)[Name];
+ for (unsigned I = 0, N = Entries.size(); I != N; ++I) {
+ if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
+ return;
+ }
+
+ // We have not seen this entry before; record it.
+ Entry E = { Kind, Loc };
+ Entries.push_back(E);
+ }
+
+ void mergeCurHeaderContents() {
+ for (DenseMap<const FileEntry *, HeaderContents>::iterator
+ H = CurHeaderContents.begin(),
+ HEnd = CurHeaderContents.end();
+ H != HEnd; ++H) {
+ // Sort contents.
+ std::sort(H->second.begin(), H->second.end());
+
+ // Check whether we've seen this header before.
+ DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
+ AllHeaderContents.find(H->first);
+ if (KnownH == AllHeaderContents.end()) {
+ // We haven't seen this header before; record its contents.
+ AllHeaderContents.insert(*H);
+ continue;
+ }
+
+ // If the header contents are the same, we're done.
+ if (H->second == KnownH->second)
+ continue;
+
+ // Determine what changed.
+ std::set_symmetric_difference(
+ H->second.begin(), H->second.end(), KnownH->second.begin(),
+ KnownH->second.end(),
+ std::back_inserter(HeaderContentMismatches[H->first]));
+ }
+
+ CurHeaderContents.clear();
+ }
+
+private:
+ DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
+ DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
+};
+
+class CollectEntitiesVisitor
+ : public RecursiveASTVisitor<CollectEntitiesVisitor> {
+public:
+ CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities,
+ Preprocessor &PP, PreprocessorTracker &PPTracker,
+ int &HadErrors)
+ : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
+ HadErrors(HadErrors) {}
+
+ bool TraverseStmt(Stmt *S) { return true; }
+ bool TraverseType(QualType T) { return true; }
+ bool TraverseTypeLoc(TypeLoc TL) { return true; }
+ bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
+ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+ return true;
+ }
+ bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
+ return true;
+ }
+ bool TraverseTemplateName(TemplateName Template) { return true; }
+ bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
+ bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+ return true;
+ }
+ bool TraverseTemplateArguments(const TemplateArgument *Args,
+ unsigned NumArgs) {
+ return true;
+ }
+ bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
+ bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+ Expr *Init) {
+ return true;
+ }
+
+ // Check 'extern "*" {}' block for #include directives.
+ bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
+ // Bail if not a block.
+ if (!D->hasBraces())
+ return true;
+ SourceRange BlockRange = D->getSourceRange();
+ const char *LinkageLabel;
+ switch (D->getLanguage()) {
+ case LinkageSpecDecl::lang_c:
+ LinkageLabel = "extern \"C\" {}";
+ break;
+ case LinkageSpecDecl::lang_cxx:
+ LinkageLabel = "extern \"C++\" {}";
+ break;
+ }
+ if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, LinkageLabel,
+ errs()))
+ HadErrors = 1;
+ return true;
+ }
+
+ // Check 'namespace (name) {}' block for #include directives.
+ bool VisitNamespaceDecl(const NamespaceDecl *D) {
+ SourceRange BlockRange = D->getSourceRange();
+ std::string Label("namespace ");
+ Label += D->getName();
+ Label += " {}";
+ if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, Label.c_str(),
+ errs()))
+ HadErrors = 1;
+ return true;
+ }
+
+ // Collect definition entities.
+ bool VisitNamedDecl(NamedDecl *ND) {
+ // We only care about file-context variables.
+ if (!ND->getDeclContext()->isFileContext())
+ return true;
+
+ // Skip declarations that tend to be properly multiply-declared.
+ if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
+ isa<NamespaceAliasDecl>(ND) ||
+ isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
+ isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||
+ isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||
+ isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
+ (isa<TagDecl>(ND) &&
+ !cast<TagDecl>(ND)->isThisDeclarationADefinition()))
+ return true;
+
+ // Skip anonymous declarations.
+ if (!ND->getDeclName())
+ return true;
+
+ // Get the qualified name.
+ std::string Name;
+ llvm::raw_string_ostream OS(Name);
+ ND->printQualifiedName(OS);
+ OS.flush();
+ if (Name.empty())
+ return true;
+
+ Location Loc(SM, ND->getLocation());
+ if (!Loc)
+ return true;
+
+ Entities.add(Name, isa<TagDecl>(ND) ? Entry::EK_Tag : Entry::EK_Value, Loc);
+ return true;
+ }
+
+private:
+ SourceManager &SM;
+ EntityMap &Entities;
+ Preprocessor &PP;
+ PreprocessorTracker &PPTracker;
+ int &HadErrors;
+};
+
+class CollectEntitiesConsumer : public ASTConsumer {
+public:
+ CollectEntitiesConsumer(EntityMap &Entities,
+ PreprocessorTracker &preprocessorTracker,
+ Preprocessor &PP, StringRef InFile, int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker), PP(PP),
+ HadErrors(HadErrors) {
+ PPTracker.handlePreprocessorEntry(PP, InFile);
+ }
+
+ ~CollectEntitiesConsumer() override { PPTracker.handlePreprocessorExit(); }
+
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ SourceManager &SM = Ctx.getSourceManager();
+
+ // Collect declared entities.
+ CollectEntitiesVisitor(SM, Entities, PP, PPTracker, HadErrors)
+ .TraverseDecl(Ctx.getTranslationUnitDecl());
+
+ // Collect macro definitions.
+ for (Preprocessor::macro_iterator M = PP.macro_begin(),
+ MEnd = PP.macro_end();
+ M != MEnd; ++M) {
+ Location Loc(SM, M->second.getLatest()->getLocation());
+ if (!Loc)
+ continue;
+
+ Entities.add(M->first->getName().str(), Entry::EK_Macro, Loc);
+ }
+
+ // Merge header contents.
+ Entities.mergeCurHeaderContents();
+ }
+
+private:
+ EntityMap &Entities;
+ PreprocessorTracker &PPTracker;
+ Preprocessor &PP;
+ int &HadErrors;
+};
+
+class CollectEntitiesAction : public SyntaxOnlyAction {
+public:
+ CollectEntitiesAction(EntityMap &Entities,
+ PreprocessorTracker &preprocessorTracker,
+ int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker),
+ HadErrors(HadErrors) {}
+
+protected:
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+ return llvm::make_unique<CollectEntitiesConsumer>(
+ Entities, PPTracker, CI.getPreprocessor(), InFile, HadErrors);
+ }
+
+private:
+ EntityMap &Entities;
+ PreprocessorTracker &PPTracker;
+ int &HadErrors;
+};
+
+class ModularizeFrontendActionFactory : public FrontendActionFactory {
+public:
+ ModularizeFrontendActionFactory(EntityMap &Entities,
+ PreprocessorTracker &preprocessorTracker,
+ int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker),
+ HadErrors(HadErrors) {}
+
+ CollectEntitiesAction *create() override {
+ return new CollectEntitiesAction(Entities, PPTracker, HadErrors);
+ }
+
+private:
+ EntityMap &Entities;
+ PreprocessorTracker &PPTracker;
+ int &HadErrors;
+};
+
+class CompileCheckVisitor
+ : public RecursiveASTVisitor<CompileCheckVisitor> {
+public:
+ CompileCheckVisitor() {}
+
+ bool TraverseStmt(Stmt *S) { return true; }
+ bool TraverseType(QualType T) { return true; }
+ bool TraverseTypeLoc(TypeLoc TL) { return true; }
+ bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
+ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+ return true;
+ }
+ bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
+ return true;
+ }
+ bool TraverseTemplateName(TemplateName Template) { return true; }
+ bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
+ bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+ return true;
+ }
+ bool TraverseTemplateArguments(const TemplateArgument *Args,
+ unsigned NumArgs) {
+ return true;
+ }
+ bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
+ bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+ Expr *Init) {
+ return true;
+ }
+
+ // Check 'extern "*" {}' block for #include directives.
+ bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
+ return true;
+ }
+
+ // Check 'namespace (name) {}' block for #include directives.
+ bool VisitNamespaceDecl(const NamespaceDecl *D) {
+ return true;
+ }
+
+ // Collect definition entities.
+ bool VisitNamedDecl(NamedDecl *ND) {
+ return true;
+ }
+};
+
+class CompileCheckConsumer : public ASTConsumer {
+public:
+ CompileCheckConsumer() {}
+
+ void HandleTranslationUnit(ASTContext &Ctx) override {
+ CompileCheckVisitor().TraverseDecl(Ctx.getTranslationUnitDecl());
+ }
+};
+
+class CompileCheckAction : public SyntaxOnlyAction {
+public:
+ CompileCheckAction() {}
+
+protected:
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+ return llvm::make_unique<CompileCheckConsumer>();
+ }
+};
+
+class CompileCheckFrontendActionFactory : public FrontendActionFactory {
+public:
+ CompileCheckFrontendActionFactory() {}
+
+ CompileCheckAction *create() override {
+ return new CompileCheckAction();
+ }
+};
+
+int main(int Argc, const char **Argv) {
+
+ // Save program name for error messages.
+ Argv0 = Argv[0];
+
+ // Save program arguments for use in module.modulemap comment.
+ CommandLine = sys::path::stem(sys::path::filename(Argv0));
+ for (int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
+ CommandLine.append(" ");
+ CommandLine.append(Argv[ArgIndex]);
+ }
+
+ // This causes options to be parsed.
+ cl::ParseCommandLineOptions(Argc, Argv, "modularize.\n");
+
+ // No go if we have no header list file.
+ if (ListFileNames.size() == 0) {
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ std::unique_ptr<ModularizeUtilities> ModUtil;
+ int HadErrors = 0;
+
+ ModUtil.reset(
+ ModularizeUtilities::createModularizeUtilities(
+ ListFileNames, HeaderPrefix, ProblemFilesList));
+
+ // Get header file names and dependencies.
+ if (ModUtil->loadAllHeaderListsAndDependencies())
+ HadErrors = 1;
+
+ // If we are in assistant mode, output the module map and quit.
+ if (ModuleMapPath.length() != 0) {
+ if (!createModuleMap(ModuleMapPath, ModUtil->HeaderFileNames,
+ ModUtil->ProblemFileNames,
+ ModUtil->Dependencies, HeaderPrefix, RootModule))
+ return 1; // Failed.
+ return 0; // Success - Skip checks in assistant mode.
+ }
+
+ // If we're doing module maps.
+ if (!NoCoverageCheck && ModUtil->HasModuleMap) {
+ // Do coverage check.
+ if (ModUtil->doCoverageCheck(IncludePaths, CommandLine))
+ HadErrors = 1;
+ }
+
+ // Bail early if only doing the coverage check.
+ if (CoverageCheckOnly)
+ return HadErrors;
+
+ // Create the compilation database.
+ SmallString<256> PathBuf;
+ sys::fs::current_path(PathBuf);
+ std::unique_ptr<CompilationDatabase> Compilations;
+ Compilations.reset(
+ new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
+
+ // Create preprocessor tracker, to watch for macro and conditional problems.
+ std::unique_ptr<PreprocessorTracker> PPTracker(
+ PreprocessorTracker::create(ModUtil->HeaderFileNames,
+ BlockCheckHeaderListOnly));
+
+ // Coolect entities here.
+ EntityMap Entities;
+
+ // Because we can't easily determine which files failed
+ // during the tool run, if we're collecting the file lists
+ // for display, we do a first compile pass on individual
+ // files to find which ones don't compile stand-alone.
+ if (DisplayFileLists) {
+ // First, make a pass to just get compile errors.
+ for (auto &CompileCheckFile : ModUtil->HeaderFileNames) {
+ llvm::SmallVector<std::string, 32> CompileCheckFileArray;
+ CompileCheckFileArray.push_back(CompileCheckFile);
+ ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);
+ CompileCheckTool.appendArgumentsAdjuster(
+ getModularizeArgumentsAdjuster(ModUtil->Dependencies));
+ int CompileCheckFileErrors = 0;
+ CompileCheckFrontendActionFactory CompileCheckFactory;
+ CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
+ if (CompileCheckFileErrors != 0) {
+ ModUtil->addUniqueProblemFile(CompileCheckFile); // Save problem file.
+ HadErrors |= 1;
+ }
+ else
+ ModUtil->addNoCompileErrorsFile(CompileCheckFile); // Save good file.
+ }
+ }
+
+ // Then we make another pass on the good files to do the rest of the work.
+ ClangTool Tool(*Compilations,
+ (DisplayFileLists ? ModUtil->GoodFileNames : ModUtil->HeaderFileNames));
+ Tool.appendArgumentsAdjuster(
+ getModularizeArgumentsAdjuster(ModUtil->Dependencies));
+ ModularizeFrontendActionFactory Factory(Entities, *PPTracker, HadErrors);
+ HadErrors |= Tool.run(&Factory);
+
+ // Create a place to save duplicate entity locations, separate bins per kind.
+ typedef SmallVector<Location, 8> LocationArray;
+ typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
+ EntryBinArray EntryBins;
+ int KindIndex;
+ for (KindIndex = 0; KindIndex < Entry::EK_NumberOfKinds; ++KindIndex) {
+ LocationArray Array;
+ EntryBins.push_back(Array);
+ }
+
+ // Check for the same entity being defined in multiple places.
+ for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
+ E != EEnd; ++E) {
+ // If only one occurrence, exit early.
+ if (E->second.size() == 1)
+ continue;
+ // Clear entity locations.
+ for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
+ CI != CE; ++CI) {
+ CI->clear();
+ }
+ // Walk the entities of a single name, collecting the locations,
+ // separated into separate bins.
+ for (unsigned I = 0, N = E->second.size(); I != N; ++I) {
+ EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
+ }
+ // Report any duplicate entity definition errors.
+ int KindIndex = 0;
+ for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
+ DI != DE; ++DI, ++KindIndex) {
+ int ECount = DI->size();
+ // If only 1 occurrence of this entity, skip it, we only report duplicates.
+ if (ECount <= 1)
+ continue;
+ LocationArray::iterator FI = DI->begin();
+ StringRef kindName = Entry::getKindName((Entry::EntryKind)KindIndex);
+ errs() << "error: " << kindName << " '" << E->first()
+ << "' defined at multiple locations:\n";
+ for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
+ errs() << " " << FI->File->getName() << ":" << FI->Line << ":"
+ << FI->Column << "\n";
+ ModUtil->addUniqueProblemFile(FI->File->getName());
+ }
+ HadErrors = 1;
+ }
+ }
+
+ // Complain about macro instance in header files that differ based on how
+ // they are included.
+ if (PPTracker->reportInconsistentMacros(errs()))
+ HadErrors = 1;
+
+ // Complain about preprocessor conditional directives in header files that
+ // differ based on how they are included.
+ if (PPTracker->reportInconsistentConditionals(errs()))
+ HadErrors = 1;
+
+ // Complain about any headers that have contents that differ based on how
+ // they are included.
+ // FIXME: Could we provide information about which preprocessor conditionals
+ // are involved?
+ for (DenseMap<const FileEntry *, HeaderContents>::iterator
+ H = Entities.HeaderContentMismatches.begin(),
+ HEnd = Entities.HeaderContentMismatches.end();
+ H != HEnd; ++H) {
+ if (H->second.empty()) {
+ errs() << "internal error: phantom header content mismatch\n";
+ continue;
+ }
+
+ HadErrors = 1;
+ ModUtil->addUniqueProblemFile(H->first->getName());
+ errs() << "error: header '" << H->first->getName()
+ << "' has different contents depending on how it was included.\n";
+ for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
+ errs() << "note: '" << H->second[I].Name << "' in "
+ << H->second[I].Loc.File->getName() << " at "
+ << H->second[I].Loc.Line << ":" << H->second[I].Loc.Column
+ << " not always provided\n";
+ }
+ }
+
+ if (DisplayFileLists) {
+ ModUtil->displayProblemFiles();
+ ModUtil->displayGoodFiles();
+ ModUtil->displayCombinedFiles();
+ }
+
+ return HadErrors;
+}
--- /dev/null
+//===--- Modularize.h - Common definitions for Modularize -*- C++ -*-----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Common definitions for Modularize.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZE_H
+#define MODULARIZE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+// Save the program name for error messages.
+extern const char *Argv0;
+// Save the command line for comments.
+extern std::string CommandLine;
+
+// Dependency types.
+typedef llvm::SmallVector<std::string, 4> DependentsVector;
+typedef llvm::StringMap<DependentsVector> DependencyMap;
+
+// Global function declarations.
+
+/// Create the module map file.
+/// \param ModuleMapPath The path to the module map file to be generated.
+/// \param HeaderFileNames The list of header files, absolute native paths.
+/// \param ProblemFileNames The list of problem header files.
+/// \param Dependencies Map of headers that depend on other headers.
+/// \param HeaderPrefix Tells the code where the headers are, if they
+/// aren's in the current directory, allowing the generator to strip
+/// the leading, non-relative beginning of the header paths.
+/// \brief RootModuleName If not empty, specifies that a root module
+/// should be created with this name.
+/// \returns True if successful.
+bool createModuleMap(llvm::StringRef ModuleMapPath,
+ llvm::ArrayRef<std::string> HeaderFileNames,
+ llvm::ArrayRef<std::string> ProblemFileNames,
+ DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
+ llvm::StringRef RootModuleName);
+
+#endif // MODULARIZE_H
--- /dev/null
+//===--- extra/modularize/ModularizeUtilities.cpp -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a class for loading and validating a module map or
+// header list by checking that all headers in the corresponding directories
+// are accounted for.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "CoverageChecker.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "ModularizeUtilities.h"
+
+using namespace clang;
+using namespace llvm;
+using namespace Modularize;
+
+namespace {
+// Subclass TargetOptions so we can construct it inline with
+// the minimal option, the triple.
+class ModuleMapTargetOptions : public clang::TargetOptions {
+public:
+ ModuleMapTargetOptions() { Triple = llvm::sys::getDefaultTargetTriple(); }
+};
+} // namespace
+
+// ModularizeUtilities class implementation.
+
+// Constructor.
+ModularizeUtilities::ModularizeUtilities(std::vector<std::string> &InputPaths,
+ llvm::StringRef Prefix,
+ llvm::StringRef ProblemFilesListPath)
+ : InputFilePaths(InputPaths), HeaderPrefix(Prefix),
+ ProblemFilesPath(ProblemFilesListPath), HasModuleMap(false),
+ MissingHeaderCount(0),
+ // Init clang stuff needed for loading the module map and preprocessing.
+ LangOpts(new LangOptions()), DiagIDs(new DiagnosticIDs()),
+ DiagnosticOpts(new DiagnosticOptions()),
+ DC(llvm::errs(), DiagnosticOpts.get()),
+ Diagnostics(
+ new DiagnosticsEngine(DiagIDs, DiagnosticOpts.get(), &DC, false)),
+ TargetOpts(new ModuleMapTargetOptions()),
+ Target(TargetInfo::CreateTargetInfo(*Diagnostics, TargetOpts)),
+ FileMgr(new FileManager(FileSystemOpts)),
+ SourceMgr(new SourceManager(*Diagnostics, *FileMgr, false)),
+ HeaderInfo(new HeaderSearch(std::make_shared<HeaderSearchOptions>(),
+ *SourceMgr, *Diagnostics, *LangOpts,
+ Target.get())) {}
+
+// Create instance of ModularizeUtilities, to simplify setting up
+// subordinate objects.
+ModularizeUtilities *ModularizeUtilities::createModularizeUtilities(
+ std::vector<std::string> &InputPaths, llvm::StringRef Prefix,
+ llvm::StringRef ProblemFilesListPath) {
+
+ return new ModularizeUtilities(InputPaths, Prefix, ProblemFilesListPath);
+}
+
+// Load all header lists and dependencies.
+std::error_code ModularizeUtilities::loadAllHeaderListsAndDependencies() {
+ // For each input file.
+ for (auto I = InputFilePaths.begin(), E = InputFilePaths.end(); I != E; ++I) {
+ llvm::StringRef InputPath = *I;
+ // If it's a module map.
+ if (InputPath.endswith(".modulemap")) {
+ // Load the module map.
+ if (std::error_code EC = loadModuleMap(InputPath))
+ return EC;
+ }
+ else {
+ // Else we assume it's a header list and load it.
+ if (std::error_code EC = loadSingleHeaderListsAndDependencies(InputPath)) {
+ errs() << "modularize: error: Unable to get header list '" << InputPath
+ << "': " << EC.message() << '\n';
+ return EC;
+ }
+ }
+ }
+ // If we have a problem files list.
+ if (ProblemFilesPath.size() != 0) {
+ // Load problem files list.
+ if (std::error_code EC = loadProblemHeaderList(ProblemFilesPath)) {
+ errs() << "modularize: error: Unable to get problem header list '" << ProblemFilesPath
+ << "': " << EC.message() << '\n';
+ return EC;
+ }
+ }
+ return std::error_code();
+}
+
+// Do coverage checks.
+// For each loaded module map, do header coverage check.
+// Starting from the directory of the module.map file,
+// Find all header files, optionally looking only at files
+// covered by the include path options, and compare against
+// the headers referenced by the module.map file.
+// Display warnings for unaccounted-for header files.
+// Returns 0 if there were no errors or warnings, 1 if there
+// were warnings, 2 if any other problem, such as a bad
+// module map path argument was specified.
+std::error_code ModularizeUtilities::doCoverageCheck(
+ std::vector<std::string> &IncludePaths,
+ llvm::ArrayRef<std::string> CommandLine) {
+ int ModuleMapCount = ModuleMaps.size();
+ int ModuleMapIndex;
+ std::error_code EC;
+ for (ModuleMapIndex = 0; ModuleMapIndex < ModuleMapCount; ++ModuleMapIndex) {
+ std::unique_ptr<clang::ModuleMap> &ModMap = ModuleMaps[ModuleMapIndex];
+ auto Checker = CoverageChecker::createCoverageChecker(
+ InputFilePaths[ModuleMapIndex], IncludePaths, CommandLine,
+ ModMap.get());
+ std::error_code LocalEC = Checker->doChecks();
+ if (LocalEC.value() > 0)
+ EC = LocalEC;
+ }
+ return EC;
+}
+
+// Load single header list and dependencies.
+std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies(
+ llvm::StringRef InputPath) {
+
+ // By default, use the path component of the list file name.
+ SmallString<256> HeaderDirectory(InputPath);
+ llvm::sys::path::remove_filename(HeaderDirectory);
+ SmallString<256> CurrentDirectory;
+ llvm::sys::fs::current_path(CurrentDirectory);
+
+ // Get the prefix if we have one.
+ if (HeaderPrefix.size() != 0)
+ HeaderDirectory = HeaderPrefix;
+
+ // Read the header list file into a buffer.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
+ MemoryBuffer::getFile(InputPath);
+ if (std::error_code EC = listBuffer.getError())
+ return EC;
+
+ // Parse the header list into strings.
+ SmallVector<StringRef, 32> Strings;
+ listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
+
+ // Collect the header file names from the string list.
+ for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
+ E = Strings.end();
+ I != E; ++I) {
+ StringRef Line = I->trim();
+ // Ignore comments and empty lines.
+ if (Line.empty() || (Line[0] == '#'))
+ continue;
+ std::pair<StringRef, StringRef> TargetAndDependents = Line.split(':');
+ SmallString<256> HeaderFileName;
+ // Prepend header file name prefix if it's not absolute.
+ if (llvm::sys::path::is_absolute(TargetAndDependents.first))
+ llvm::sys::path::native(TargetAndDependents.first, HeaderFileName);
+ else {
+ if (HeaderDirectory.size() != 0)
+ HeaderFileName = HeaderDirectory;
+ else
+ HeaderFileName = CurrentDirectory;
+ llvm::sys::path::append(HeaderFileName, TargetAndDependents.first);
+ llvm::sys::path::native(HeaderFileName);
+ }
+ // Handle optional dependencies.
+ DependentsVector Dependents;
+ SmallVector<StringRef, 4> DependentsList;
+ TargetAndDependents.second.split(DependentsList, " ", -1, false);
+ int Count = DependentsList.size();
+ for (int Index = 0; Index < Count; ++Index) {
+ SmallString<256> Dependent;
+ if (llvm::sys::path::is_absolute(DependentsList[Index]))
+ Dependent = DependentsList[Index];
+ else {
+ if (HeaderDirectory.size() != 0)
+ Dependent = HeaderDirectory;
+ else
+ Dependent = CurrentDirectory;
+ llvm::sys::path::append(Dependent, DependentsList[Index]);
+ }
+ llvm::sys::path::native(Dependent);
+ Dependents.push_back(getCanonicalPath(Dependent.str()));
+ }
+ // Get canonical form.
+ HeaderFileName = getCanonicalPath(HeaderFileName);
+ // Save the resulting header file path and dependencies.
+ HeaderFileNames.push_back(HeaderFileName.str());
+ Dependencies[HeaderFileName.str()] = Dependents;
+ }
+ return std::error_code();
+}
+
+// Load problem header list.
+std::error_code ModularizeUtilities::loadProblemHeaderList(
+ llvm::StringRef InputPath) {
+
+ // By default, use the path component of the list file name.
+ SmallString<256> HeaderDirectory(InputPath);
+ llvm::sys::path::remove_filename(HeaderDirectory);
+ SmallString<256> CurrentDirectory;
+ llvm::sys::fs::current_path(CurrentDirectory);
+
+ // Get the prefix if we have one.
+ if (HeaderPrefix.size() != 0)
+ HeaderDirectory = HeaderPrefix;
+
+ // Read the header list file into a buffer.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
+ MemoryBuffer::getFile(InputPath);
+ if (std::error_code EC = listBuffer.getError())
+ return EC;
+
+ // Parse the header list into strings.
+ SmallVector<StringRef, 32> Strings;
+ listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
+
+ // Collect the header file names from the string list.
+ for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
+ E = Strings.end();
+ I != E; ++I) {
+ StringRef Line = I->trim();
+ // Ignore comments and empty lines.
+ if (Line.empty() || (Line[0] == '#'))
+ continue;
+ SmallString<256> HeaderFileName;
+ // Prepend header file name prefix if it's not absolute.
+ if (llvm::sys::path::is_absolute(Line))
+ llvm::sys::path::native(Line, HeaderFileName);
+ else {
+ if (HeaderDirectory.size() != 0)
+ HeaderFileName = HeaderDirectory;
+ else
+ HeaderFileName = CurrentDirectory;
+ llvm::sys::path::append(HeaderFileName, Line);
+ llvm::sys::path::native(HeaderFileName);
+ }
+ // Get canonical form.
+ HeaderFileName = getCanonicalPath(HeaderFileName);
+ // Save the resulting header file path.
+ ProblemFileNames.push_back(HeaderFileName.str());
+ }
+ return std::error_code();
+}
+
+// Load single module map and extract header file list.
+std::error_code ModularizeUtilities::loadModuleMap(
+ llvm::StringRef InputPath) {
+ // Get file entry for module.modulemap file.
+ const FileEntry *ModuleMapEntry =
+ SourceMgr->getFileManager().getFile(InputPath);
+
+ // return error if not found.
+ if (!ModuleMapEntry) {
+ llvm::errs() << "error: File \"" << InputPath << "\" not found.\n";
+ return std::error_code(1, std::generic_category());
+ }
+
+ // Because the module map parser uses a ForwardingDiagnosticConsumer,
+ // which doesn't forward the BeginSourceFile call, we do it explicitly here.
+ DC.BeginSourceFile(*LangOpts, nullptr);
+
+ // Figure out the home directory for the module map file.
+ const DirectoryEntry *Dir = ModuleMapEntry->getDir();
+ StringRef DirName(Dir->getName());
+ if (llvm::sys::path::filename(DirName) == "Modules") {
+ DirName = llvm::sys::path::parent_path(DirName);
+ if (DirName.endswith(".framework"))
+ Dir = FileMgr->getDirectory(DirName);
+ // FIXME: This assert can fail if there's a race between the above check
+ // and the removal of the directory.
+ assert(Dir && "parent must exist");
+ }
+
+ std::unique_ptr<ModuleMap> ModMap;
+ ModMap.reset(new ModuleMap(*SourceMgr, *Diagnostics, *LangOpts,
+ Target.get(), *HeaderInfo));
+
+ // Parse module.modulemap file into module map.
+ if (ModMap->parseModuleMapFile(ModuleMapEntry, false, Dir)) {
+ return std::error_code(1, std::generic_category());
+ }
+
+ // Do matching end call.
+ DC.EndSourceFile();
+
+ // Reset missing header count.
+ MissingHeaderCount = 0;
+
+ if (!collectModuleMapHeaders(ModMap.get()))
+ return std::error_code(1, std::generic_category());
+
+ // Save module map.
+ ModuleMaps.push_back(std::move(ModMap));
+
+ // Indicate we are using module maps.
+ HasModuleMap = true;
+
+ // Return code of 1 for missing headers.
+ if (MissingHeaderCount)
+ return std::error_code(1, std::generic_category());
+
+ return std::error_code();
+}
+
+// Collect module map headers.
+// Walks the modules and collects referenced headers into
+// HeaderFileNames.
+bool ModularizeUtilities::collectModuleMapHeaders(clang::ModuleMap *ModMap) {
+ for (ModuleMap::module_iterator I = ModMap->module_begin(),
+ E = ModMap->module_end();
+ I != E; ++I) {
+ if (!collectModuleHeaders(*I->second))
+ return false;
+ }
+ return true;
+}
+
+// Collect referenced headers from one module.
+// Collects the headers referenced in the given module into
+// HeaderFileNames.
+bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) {
+
+ // Ignore explicit modules because they often have dependencies
+ // we can't know.
+ if (Mod.IsExplicit)
+ return true;
+
+ // Treat headers in umbrella directory as dependencies.
+ DependentsVector UmbrellaDependents;
+
+ // Recursively do submodules.
+ for (auto MI = Mod.submodule_begin(), MIEnd = Mod.submodule_end();
+ MI != MIEnd; ++MI)
+ collectModuleHeaders(**MI);
+
+ if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
+ std::string HeaderPath = getCanonicalPath(UmbrellaHeader->getName());
+ // Collect umbrella header.
+ HeaderFileNames.push_back(HeaderPath);
+
+ // FUTURE: When needed, umbrella header header collection goes here.
+ }
+ else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
+ // If there normal headers, assume these are umbrellas and skip collection.
+ if (Mod.Headers->size() == 0) {
+ // Collect headers in umbrella directory.
+ if (!collectUmbrellaHeaders(UmbrellaDir->getName(), UmbrellaDependents))
+ return false;
+ }
+ }
+
+ // We ignore HK_Private, HK_Textual, HK_PrivateTextual, and HK_Excluded,
+ // assuming they are marked as such either because of unsuitability for
+ // modules or because they are meant to be included by another header,
+ // and thus should be ignored by modularize.
+
+ int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size();
+
+ for (int Index = 0; Index < NormalHeaderCount; ++Index) {
+ DependentsVector NormalDependents;
+ // Collect normal header.
+ const clang::Module::Header &Header(
+ Mod.Headers[clang::Module::HK_Normal][Index]);
+ std::string HeaderPath = getCanonicalPath(Header.Entry->getName());
+ HeaderFileNames.push_back(HeaderPath);
+ }
+
+ int MissingCountThisModule = Mod.MissingHeaders.size();
+
+ for (int Index = 0; Index < MissingCountThisModule; ++Index) {
+ std::string MissingFile = Mod.MissingHeaders[Index].FileName;
+ SourceLocation Loc = Mod.MissingHeaders[Index].FileNameLoc;
+ errs() << Loc.printToString(*SourceMgr)
+ << ": error : Header not found: " << MissingFile << "\n";
+ }
+
+ MissingHeaderCount += MissingCountThisModule;
+
+ return true;
+}
+
+// Collect headers from an umbrella directory.
+bool ModularizeUtilities::collectUmbrellaHeaders(StringRef UmbrellaDirName,
+ DependentsVector &Dependents) {
+ // Initialize directory name.
+ SmallString<256> Directory(UmbrellaDirName);
+ // Walk the directory.
+ std::error_code EC;
+ llvm::sys::fs::file_status Status;
+ for (llvm::sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
+ I.increment(EC)) {
+ if (EC)
+ return false;
+ std::string File(I->path());
+ I->status(Status);
+ llvm::sys::fs::file_type Type = Status.type();
+ // If the file is a directory, ignore the name and recurse.
+ if (Type == llvm::sys::fs::file_type::directory_file) {
+ if (!collectUmbrellaHeaders(File, Dependents))
+ return false;
+ continue;
+ }
+ // If the file does not have a common header extension, ignore it.
+ if (!isHeader(File))
+ continue;
+ // Save header name.
+ std::string HeaderPath = getCanonicalPath(File);
+ Dependents.push_back(HeaderPath);
+ }
+ return true;
+}
+
+// Replace .. embedded in path for purposes of having
+// a canonical path.
+static std::string replaceDotDot(StringRef Path) {
+ SmallString<128> Buffer;
+ llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path),
+ E = llvm::sys::path::end(Path);
+ while (B != E) {
+ if (B->compare(".") == 0) {
+ }
+ else if (B->compare("..") == 0)
+ llvm::sys::path::remove_filename(Buffer);
+ else
+ llvm::sys::path::append(Buffer, *B);
+ ++B;
+ }
+ if (Path.endswith("/") || Path.endswith("\\"))
+ Buffer.append(1, Path.back());
+ return Buffer.c_str();
+}
+
+// Convert header path to canonical form.
+// The canonical form is basically just use forward slashes, and remove "./".
+// \param FilePath The file path, relative to the module map directory.
+// \returns The file path in canonical form.
+std::string ModularizeUtilities::getCanonicalPath(StringRef FilePath) {
+ std::string Tmp(replaceDotDot(FilePath));
+ std::replace(Tmp.begin(), Tmp.end(), '\\', '/');
+ StringRef Tmp2(Tmp);
+ if (Tmp2.startswith("./"))
+ Tmp = Tmp2.substr(2);
+ return Tmp;
+}
+
+// Check for header file extension.
+// If the file extension is .h, .inc, or missing, it's
+// assumed to be a header.
+// \param FileName The file name. Must not be a directory.
+// \returns true if it has a header extension or no extension.
+bool ModularizeUtilities::isHeader(StringRef FileName) {
+ StringRef Extension = llvm::sys::path::extension(FileName);
+ if (Extension.size() == 0)
+ return true;
+ if (Extension.equals_lower(".h"))
+ return true;
+ if (Extension.equals_lower(".inc"))
+ return true;
+ return false;
+}
+
+// Get directory path component from file path.
+// \returns the component of the given path, which will be
+// relative if the given path is relative, absolute if the
+// given path is absolute, or "." if the path has no leading
+// path component.
+std::string ModularizeUtilities::getDirectoryFromPath(StringRef Path) {
+ SmallString<256> Directory(Path);
+ sys::path::remove_filename(Directory);
+ if (Directory.size() == 0)
+ return ".";
+ return Directory.str();
+}
+
+// Add unique problem file.
+// Also standardizes the path.
+void ModularizeUtilities::addUniqueProblemFile(std::string FilePath) {
+ FilePath = getCanonicalPath(FilePath);
+ // Don't add if already present.
+ for(auto &TestFilePath : ProblemFileNames) {
+ if (TestFilePath == FilePath)
+ return;
+ }
+ ProblemFileNames.push_back(FilePath);
+}
+
+// Add file with no compile errors.
+// Also standardizes the path.
+void ModularizeUtilities::addNoCompileErrorsFile(std::string FilePath) {
+ FilePath = getCanonicalPath(FilePath);
+ GoodFileNames.push_back(FilePath);
+}
+
+// List problem files.
+void ModularizeUtilities::displayProblemFiles() {
+ errs() << "\nThese are the files with possible errors:\n\n";
+ for (auto &ProblemFile : ProblemFileNames) {
+ errs() << ProblemFile << "\n";
+ }
+}
+
+// List files with no problems.
+void ModularizeUtilities::displayGoodFiles() {
+ errs() << "\nThese are the files with no detected errors:\n\n";
+ for (auto &GoodFile : HeaderFileNames) {
+ bool Good = true;
+ for (auto &ProblemFile : ProblemFileNames) {
+ if (ProblemFile == GoodFile) {
+ Good = false;
+ break;
+ }
+ }
+ if (Good)
+ errs() << GoodFile << "\n";
+ }
+}
+
+// List files with problem files commented out.
+void ModularizeUtilities::displayCombinedFiles() {
+ errs() <<
+ "\nThese are the combined files, with problem files preceded by #:\n\n";
+ for (auto &File : HeaderFileNames) {
+ bool Good = true;
+ for (auto &ProblemFile : ProblemFileNames) {
+ if (ProblemFile == File) {
+ Good = false;
+ break;
+ }
+ }
+ errs() << (Good ? "" : "#") << File << "\n";
+ }
+}
--- /dev/null
+//=====-- ModularizeUtilities.h - Utilities for modularize -*- C++ -*-======//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief ModularizeUtilities class definition.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZEUTILITIES_H
+#define MODULARIZEUTILITIES_H
+
+#include "Modularize.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/ModuleMap.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+#include <string>
+#include <vector>
+
+namespace Modularize {
+
+/// Modularize utilities class.
+/// Support functions and data for modularize.
+class ModularizeUtilities {
+public:
+ // Input arguments.
+
+ /// The input file paths.
+ std::vector<std::string> InputFilePaths;
+ /// The header prefix.
+ llvm::StringRef HeaderPrefix;
+ /// The path of problem files list file.
+ llvm::StringRef ProblemFilesPath;
+
+ // Output data.
+
+ /// List of top-level header files.
+ llvm::SmallVector<std::string, 32> HeaderFileNames;
+ /// Map of top-level header file dependencies.
+ DependencyMap Dependencies;
+ /// True if we have module maps.
+ bool HasModuleMap;
+ /// Missing header count.
+ int MissingHeaderCount;
+ /// List of header files with no problems during the first pass,
+ /// that is, no compile errors.
+ llvm::SmallVector<std::string, 32> GoodFileNames;
+ /// List of header files with problems.
+ llvm::SmallVector<std::string, 32> ProblemFileNames;
+
+ // Functions.
+
+ /// Constructor.
+ /// You can use the static createModularizeUtilities to create an instance
+ /// of this object.
+ /// \param InputPaths The input file paths.
+ /// \param Prefix The headear path prefix.
+ /// \param ProblemFilesListPath The problem header list path.
+ ModularizeUtilities(std::vector<std::string> &InputPaths,
+ llvm::StringRef Prefix,
+ llvm::StringRef ProblemFilesListPath);
+
+ /// Create instance of ModularizeUtilities.
+ /// \param InputPaths The input file paths.
+ /// \param Prefix The headear path prefix.
+ /// \param ProblemFilesListPath The problem header list path.
+ /// \returns Initialized ModularizeUtilities object.
+ static ModularizeUtilities *createModularizeUtilities(
+ std::vector<std::string> &InputPaths,
+ llvm::StringRef Prefix,
+ llvm::StringRef ProblemFilesListPath);
+
+ /// Load header list and dependencies.
+ /// \returns std::error_code.
+ std::error_code loadAllHeaderListsAndDependencies();
+
+ /// Do coverage checks.
+ /// For each loaded module map, do header coverage check.
+ /// Starting from the directory of the module.map file,
+ /// Find all header files, optionally looking only at files
+ /// covered by the include path options, and compare against
+ /// the headers referenced by the module.map file.
+ /// Display warnings for unaccounted-for header files.
+ /// \param IncludePaths The include paths to check for files.
+ /// (Note that other directories above these paths are ignored.
+ /// To expect all files to be accounted for from the module.modulemap
+ /// file directory on down, leave this empty.)
+ /// \param CommandLine Compile command line arguments.
+ /// \returns 0 if there were no errors or warnings, 1 if there
+ /// were warnings, 2 if any other problem, such as a bad
+ /// module map path argument was specified.
+ std::error_code doCoverageCheck(std::vector<std::string> &IncludePaths,
+ llvm::ArrayRef<std::string> CommandLine);
+
+ /// Add unique problem file.
+ /// Also standardizes the path.
+ /// \param FilePath Problem file path.
+ void addUniqueProblemFile(std::string FilePath);
+
+ /// Add file with no compile errors.
+ /// Also standardizes the path.
+ /// \param FilePath Problem file path.
+ void addNoCompileErrorsFile(std::string FilePath);
+
+ /// List problem files.
+ void displayProblemFiles();
+
+ /// List files with no problems.
+ void displayGoodFiles();
+
+ /// List files with problem files commented out.
+ void displayCombinedFiles();
+
+ // Internal.
+
+protected:
+
+ /// Load single header list and dependencies.
+ /// \param InputPath The input file path.
+ /// \returns std::error_code.
+ std::error_code loadSingleHeaderListsAndDependencies(
+ llvm::StringRef InputPath);
+
+ /// Load problem header list.
+ /// \param InputPath The input file path.
+ /// \returns std::error_code.
+ std::error_code loadProblemHeaderList(
+ llvm::StringRef InputPath);
+
+ /// Load single module map and extract header file list.
+ /// \param InputPath The input file path.
+ /// \returns std::error_code.
+ std::error_code loadModuleMap(
+ llvm::StringRef InputPath);
+
+ /// Collect module Map headers.
+ /// Walks the modules and collects referenced headers into
+ /// HeaderFileNames.
+ /// \param ModMap A loaded module map object.
+ /// \return True if no errors.
+ bool collectModuleMapHeaders(clang::ModuleMap *ModMap);
+
+ /// Collect referenced headers from one module.
+ /// Collects the headers referenced in the given module into
+ /// HeaderFileNames.
+ /// \param Mod The module reference.
+ /// \return True if no errors.
+ bool collectModuleHeaders(const clang::Module &Mod);
+
+ /// Collect headers from an umbrella directory.
+ /// \param UmbrellaDirName The umbrella directory name.
+ /// \return True if no errors.
+ bool collectUmbrellaHeaders(llvm::StringRef UmbrellaDirName,
+ DependentsVector &Dependents);
+
+public:
+
+ // Utility functions.
+
+ /// Convert header path to canonical form.
+ /// The canonical form is basically just use forward slashes,
+ /// and remove "./".
+ /// \param FilePath The file path.
+ /// \returns The file path in canonical form.
+ static std::string getCanonicalPath(llvm::StringRef FilePath);
+
+ /// Check for header file extension.
+ /// If the file extension is .h, .inc, or missing, it's
+ /// assumed to be a header.
+ /// \param FileName The file name. Must not be a directory.
+ /// \returns true if it has a header extension or no extension.
+ static bool isHeader(llvm::StringRef FileName);
+
+ /// Get directory path component from file path.
+ /// \returns the component of the given path, which will be
+ /// relative if the given path is relative, absolute if the
+ /// given path is absolute, or "." if the path has no leading
+ /// path component.
+ static std::string getDirectoryFromPath(llvm::StringRef Path);
+
+ // Internal data.
+
+ /// Options controlling the language variant.
+ std::shared_ptr<clang::LangOptions> LangOpts;
+ /// Diagnostic IDs.
+ const llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs;
+ /// Options controlling the diagnostic engine.
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagnosticOpts;
+ /// Diagnostic consumer.
+ clang::TextDiagnosticPrinter DC;
+ /// Diagnostic engine.
+ llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> Diagnostics;
+ /// Options controlling the target.
+ std::shared_ptr<clang::TargetOptions> TargetOpts;
+ /// Target information.
+ llvm::IntrusiveRefCntPtr<clang::TargetInfo> Target;
+ /// Options controlling the file system manager.
+ clang::FileSystemOptions FileSystemOpts;
+ /// File system manager.
+ llvm::IntrusiveRefCntPtr<clang::FileManager> FileMgr;
+ /// Source manager.
+ llvm::IntrusiveRefCntPtr<clang::SourceManager> SourceMgr;
+ /// Header search manager.
+ std::unique_ptr<clang::HeaderSearch> HeaderInfo;
+ // The loaded module map objects.
+ std::vector<std::unique_ptr<clang::ModuleMap>> ModuleMaps;
+};
+
+} // end namespace Modularize
+
+#endif // MODULARIZEUTILITIES_H
--- /dev/null
+//===--- ModuleAssistant.cpp - Module map generation manager -*- C++ -*---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This file defines the module generation entry point function,
+// createModuleMap, a Module class for representing a module,
+// and various implementation functions for doing the underlying
+// work, described below.
+//
+// The "Module" class represents a module, with members for storing the module
+// name, associated header file names, and sub-modules, and an "output"
+// function that recursively writes the module definitions.
+//
+// The "createModuleMap" function implements the top-level logic of the
+// assistant mode. It calls a loadModuleDescriptions function to walk
+// the header list passed to it and creates a tree of Module objects
+// representing the module hierarchy, represented by a "Module" object,
+// the "RootModule". This root module may or may not represent an actual
+// module in the module map, depending on the "--root-module" option passed
+// to modularize. It then calls a writeModuleMap function to set up the
+// module map file output and walk the module tree, outputting the module
+// map file using a stream obtained and managed by an
+// llvm::tool_output_file object.
+//
+//===---------------------------------------------------------------------===//
+
+#include "Modularize.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <vector>
+
+// Local definitions:
+
+namespace {
+
+// Internal class definitions:
+
+// Represents a module.
+class Module {
+public:
+ Module(llvm::StringRef Name, bool Problem);
+ Module();
+ ~Module();
+ bool output(llvm::raw_fd_ostream &OS, int Indent);
+ Module *findSubModule(llvm::StringRef SubName);
+
+public:
+ std::string Name;
+ std::vector<std::string> HeaderFileNames;
+ std::vector<Module *> SubModules;
+ bool IsProblem;
+};
+
+} // end anonymous namespace.
+
+// Module functions:
+
+// Constructors.
+Module::Module(llvm::StringRef Name, bool Problem)
+ : Name(Name), IsProblem(Problem) {}
+Module::Module() : IsProblem(false) {}
+
+// Destructor.
+Module::~Module() {
+ // Free submodules.
+ while (!SubModules.empty()) {
+ Module *last = SubModules.back();
+ SubModules.pop_back();
+ delete last;
+ }
+}
+
+// Write a module hierarchy to the given output stream.
+bool Module::output(llvm::raw_fd_ostream &OS, int Indent) {
+ // If this is not the nameless root module, start a module definition.
+ if (Name.size() != 0) {
+ OS.indent(Indent);
+ OS << "module " << Name << " {\n";
+ Indent += 2;
+ }
+
+ // Output submodules.
+ for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
+ if (!(*I)->output(OS, Indent))
+ return false;
+ }
+
+ // Output header files.
+ for (auto I = HeaderFileNames.begin(), E = HeaderFileNames.end(); I != E;
+ ++I) {
+ OS.indent(Indent);
+ if (IsProblem || strstr((*I).c_str(), ".inl"))
+ OS << "exclude header \"" << *I << "\"\n";
+ else
+ OS << "header \"" << *I << "\"\n";
+ }
+
+ // If this module has header files, output export directive.
+ if (HeaderFileNames.size() != 0) {
+ OS.indent(Indent);
+ OS << "export *\n";
+ }
+
+ // If this is not the nameless root module, close the module definition.
+ if (Name.size() != 0) {
+ Indent -= 2;
+ OS.indent(Indent);
+ OS << "}\n";
+ }
+
+ return true;
+}
+
+// Lookup a sub-module.
+Module *Module::findSubModule(llvm::StringRef SubName) {
+ for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
+ if ((*I)->Name == SubName)
+ return *I;
+ }
+ return nullptr;
+}
+
+// Implementation functions:
+
+// Reserved keywords in module.modulemap syntax.
+// Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
+// such as in ModuleMapParser::consumeToken().
+static const char *const ReservedNames[] = {
+ "config_macros", "export", "module", "conflict", "framework",
+ "requires", "exclude", "header", "private", "explicit",
+ "link", "umbrella", "extern", "use", nullptr // Flag end.
+};
+
+// Convert module name to a non-keyword.
+// Prepends a '_' to the name if and only if the name is a keyword.
+static std::string
+ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {
+ std::string SafeName = MightBeReservedName;
+ for (int Index = 0; ReservedNames[Index] != nullptr; ++Index) {
+ if (MightBeReservedName == ReservedNames[Index]) {
+ SafeName.insert(0, "_");
+ break;
+ }
+ }
+ return SafeName;
+}
+
+// Convert module name to a non-keyword.
+// Prepends a '_' to the name if and only if the name is a keyword.
+static std::string
+ensureVaidModuleName(llvm::StringRef MightBeInvalidName) {
+ std::string SafeName = MightBeInvalidName;
+ std::replace(SafeName.begin(), SafeName.end(), '-', '_');
+ std::replace(SafeName.begin(), SafeName.end(), '.', '_');
+ if (isdigit(SafeName[0]))
+ SafeName = "_" + SafeName;
+ return SafeName;
+}
+
+// Add one module, given a header file path.
+static bool addModuleDescription(Module *RootModule,
+ llvm::StringRef HeaderFilePath,
+ llvm::StringRef HeaderPrefix,
+ DependencyMap &Dependencies,
+ bool IsProblemFile) {
+ Module *CurrentModule = RootModule;
+ DependentsVector &FileDependents = Dependencies[HeaderFilePath];
+ std::string FilePath;
+ // Strip prefix.
+ // HeaderFilePath should be compared to natively-canonicalized Prefix.
+ llvm::SmallString<256> NativePath, NativePrefix;
+ llvm::sys::path::native(HeaderFilePath, NativePath);
+ llvm::sys::path::native(HeaderPrefix, NativePrefix);
+ if (NativePath.startswith(NativePrefix))
+ FilePath = NativePath.substr(NativePrefix.size() + 1);
+ else
+ FilePath = HeaderFilePath;
+ int Count = FileDependents.size();
+ // Headers that go into modules must not depend on other files being
+ // included first. If there are any dependents, warn user and omit.
+ if (Count != 0) {
+ llvm::errs() << "warning: " << FilePath
+ << " depends on other headers being included first,"
+ " meaning the module.modulemap won't compile."
+ " This header will be omitted from the module map.\n";
+ return true;
+ }
+ // Make canonical.
+ std::replace(FilePath.begin(), FilePath.end(), '\\', '/');
+ // Insert module into tree, using subdirectories as submodules.
+ for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),
+ E = llvm::sys::path::end(FilePath);
+ I != E; ++I) {
+ if ((*I)[0] == '.')
+ continue;
+ std::string Stem = llvm::sys::path::stem(*I);
+ Stem = ensureNoCollisionWithReservedName(Stem);
+ Stem = ensureVaidModuleName(Stem);
+ Module *SubModule = CurrentModule->findSubModule(Stem);
+ if (!SubModule) {
+ SubModule = new Module(Stem, IsProblemFile);
+ CurrentModule->SubModules.push_back(SubModule);
+ }
+ CurrentModule = SubModule;
+ }
+ // Add header file name to headers.
+ CurrentModule->HeaderFileNames.push_back(FilePath);
+ return true;
+}
+
+// Create the internal module tree representation.
+static Module *loadModuleDescriptions(
+ llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,
+ llvm::ArrayRef<std::string> ProblemFileNames,
+ DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {
+
+ // Create root module.
+ auto *RootModule = new Module(RootModuleName, false);
+
+ llvm::SmallString<256> CurrentDirectory;
+ llvm::sys::fs::current_path(CurrentDirectory);
+
+ // If no header prefix, use current directory.
+ if (HeaderPrefix.size() == 0)
+ HeaderPrefix = CurrentDirectory;
+
+ // Walk the header file names and output the module map.
+ for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),
+ E = HeaderFileNames.end();
+ I != E; ++I) {
+ std::string Header(*I);
+ bool IsProblemFile = false;
+ for (auto &ProblemFile : ProblemFileNames) {
+ if (ProblemFile == Header) {
+ IsProblemFile = true;
+ break;
+ }
+ }
+ // Add as a module.
+ if (!addModuleDescription(RootModule, Header, HeaderPrefix, Dependencies, IsProblemFile))
+ return nullptr;
+ }
+
+ return RootModule;
+}
+
+// Kick off the writing of the module map.
+static bool writeModuleMap(llvm::StringRef ModuleMapPath,
+ llvm::StringRef HeaderPrefix, Module *RootModule) {
+ llvm::SmallString<256> HeaderDirectory(ModuleMapPath);
+ llvm::sys::path::remove_filename(HeaderDirectory);
+ llvm::SmallString<256> FilePath;
+
+ // Get the module map file path to be used.
+ if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {
+ FilePath = HeaderPrefix;
+ // Prepend header file name prefix if it's not absolute.
+ llvm::sys::path::append(FilePath, ModuleMapPath);
+ llvm::sys::path::native(FilePath);
+ } else {
+ FilePath = ModuleMapPath;
+ llvm::sys::path::native(FilePath);
+ }
+
+ // Set up module map output file.
+ std::error_code EC;
+ llvm::tool_output_file Out(FilePath, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << Argv0 << ": error opening " << FilePath << ":"
+ << EC.message() << "\n";
+ return false;
+ }
+
+ // Get output stream from tool output buffer/manager.
+ llvm::raw_fd_ostream &OS = Out.os();
+
+ // Output file comment.
+ OS << "// " << ModuleMapPath << "\n";
+ OS << "// Generated by: " << CommandLine << "\n\n";
+
+ // Write module hierarchy from internal representation.
+ if (!RootModule->output(OS, 0))
+ return false;
+
+ // Tell tool_output_file that we want to keep the file.
+ Out.keep();
+
+ return true;
+}
+
+// Global functions:
+
+// Module map generation entry point.
+bool createModuleMap(llvm::StringRef ModuleMapPath,
+ llvm::ArrayRef<std::string> HeaderFileNames,
+ llvm::ArrayRef<std::string> ProblemFileNames,
+ DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
+ llvm::StringRef RootModuleName) {
+ // Load internal representation of modules.
+ std::unique_ptr<Module> RootModule(
+ loadModuleDescriptions(
+ RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies,
+ HeaderPrefix));
+ if (!RootModule.get())
+ return false;
+
+ // Write module map file.
+ return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule.get());
+}
--- /dev/null
+//===--- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// The Basic Idea (Macro and Conditional Checking)
+//
+// Basically we install a PPCallbacks-derived object to track preprocessor
+// activity, namely when a header file is entered/exited, when a macro
+// is expanded, when "defined" is used, and when #if, #elif, #ifdef,
+// and #ifndef are used. We save the state of macro and "defined"
+// expressions in a map, keyed on a name/file/line/column quadruple.
+// The map entries store the different states (values) that a macro expansion,
+// "defined" expression, or condition expression has in the course of
+// processing for the one location in the one header containing it,
+// plus a list of the nested include stacks for the states. When a macro
+// or "defined" expression evaluates to the same value, which is the
+// desired case, only one state is stored. Similarly, for conditional
+// directives, we save the condition expression states in a separate map.
+//
+// This information is collected as modularize compiles all the headers
+// given to it to process. After all the compilations are performed,
+// a check is performed for any entries in the maps that contain more
+// than one different state, and for these an output message is generated.
+//
+// For example:
+//
+// (...)/SubHeader.h:11:5:
+// #if SYMBOL == 1
+// ^
+// error: Macro instance 'SYMBOL' has different values in this header,
+// depending on how it was included.
+// 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+// (...)/Header1.h
+// (...)/SubHeader.h
+// (...)/SubHeader.h:3:9:
+// #define SYMBOL 1
+// ^
+// Macro defined here.
+// 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+// (...)/Header2.h
+// (...)/SubHeader.h
+// (...)/SubHeader.h:7:9:
+// #define SYMBOL 2
+// ^
+// Macro defined here.
+//
+// The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested
+// '#include' Checking)
+//
+// To check for '#include' directives nested inside 'Extern "C/C++" {}'
+// or 'namespace {}' blocks, we keep track of the '#include' directives
+// while running the preprocessor, and later during a walk of the AST
+// we call a function to check for any '#include' directies inside
+// an 'Extern "C/C++" {}' or 'namespace {}' block, given its source
+// range.
+//
+// Design and Implementation Details (Macro and Conditional Checking)
+//
+// A PreprocessorTrackerImpl class implements the PreprocessorTracker
+// interface. It uses a PreprocessorCallbacks class derived from PPCallbacks
+// to track preprocessor activity, namely entering/exiting a header, macro
+// expansions, use of "defined" expressions, and #if, #elif, #ifdef, and
+// #ifndef conditional directives. PreprocessorTrackerImpl stores a map
+// of MacroExpansionTracker objects keyed on a name/file/line/column
+// value represented by a light-weight PPItemKey value object. This
+// is the key top-level data structure tracking the values of macro
+// expansion instances. Similarly, it stores a map of ConditionalTracker
+// objects with the same kind of key, for tracking preprocessor conditional
+// directives.
+//
+// The MacroExpansionTracker object represents one macro reference or use
+// of a "defined" expression in a header file. It stores a handle to a
+// string representing the unexpanded macro instance, a handle to a string
+// representing the unpreprocessed source line containing the unexpanded
+// macro instance, and a vector of one or more MacroExpansionInstance
+// objects.
+//
+// The MacroExpansionInstance object represents one or more expansions
+// of a macro reference, for the case where the macro expands to the same
+// value. MacroExpansionInstance stores a handle to a string representing
+// the expanded macro value, a PPItemKey representing the file/line/column
+// where the macro was defined, a handle to a string representing the source
+// line containing the macro definition, and a vector of InclusionPathHandle
+// values that represents the hierarchies of include files for each case
+// where the particular header containing the macro reference was referenced
+// or included.
+
+// In the normal case where a macro instance always expands to the same
+// value, the MacroExpansionTracker object will only contain one
+// MacroExpansionInstance representing all the macro expansion instances.
+// If a case was encountered where a macro instance expands to a value
+// that is different from that seen before, or the macro was defined in
+// a different place, a new MacroExpansionInstance object representing
+// that case will be added to the vector in MacroExpansionTracker. If a
+// macro instance expands to a value already seen before, the
+// InclusionPathHandle representing that case's include file hierarchy
+// will be added to the existing MacroExpansionInstance object.
+
+// For checking conditional directives, the ConditionalTracker class
+// functions similarly to MacroExpansionTracker, but tracks an #if,
+// #elif, #ifdef, or #ifndef directive in a header file. It stores
+// a vector of one or two ConditionalExpansionInstance objects,
+// representing the cases where the conditional expression evaluates
+// to true or false. This latter object stores the evaluated value
+// of the condition expression (a bool) and a vector of
+// InclusionPathHandles.
+//
+// To reduce the instances of string and object copying, the
+// PreprocessorTrackerImpl class uses a StringPool to save all stored
+// strings, and defines a StringHandle type to abstract the references
+// to the strings.
+//
+// PreprocessorTrackerImpl also maintains a list representing the unique
+// headers, which is just a vector of StringHandle's for the header file
+// paths. A HeaderHandle abstracts a reference to a header, and is simply
+// the index of the stored header file path.
+//
+// A HeaderInclusionPath class abstracts a unique hierarchy of header file
+// inclusions. It simply stores a vector of HeaderHandles ordered from the
+// top-most header (the one from the header list passed to modularize) down
+// to the header containing the macro reference. PreprocessorTrackerImpl
+// stores a vector of these objects. An InclusionPathHandle typedef
+// abstracts a reference to one of the HeaderInclusionPath objects, and is
+// simply the index of the stored HeaderInclusionPath object. The
+// MacroExpansionInstance object stores a vector of these handles so that
+// the reporting function can display the include hierarchies for the macro
+// expansion instances represented by that object, to help the user
+// understand how the header was included. (A future enhancement might
+// be to associate a line number for the #include directives, but I
+// think not doing so is good enough for the present.)
+//
+// A key reason for using these opaque handles was to try to keep all the
+// internal objects light-weight value objects, in order to reduce string
+// and object copying overhead, and to abstract this implementation detail.
+//
+// The key data structures are built up while modularize runs the headers
+// through the compilation. A PreprocessorTracker instance is created and
+// passed down to the AST action and consumer objects in modularize. For
+// each new compilation instance, the consumer calls the
+// PreprocessorTracker's handleNewPreprocessorEntry function, which sets
+// up a PreprocessorCallbacks object for the preprocessor. At the end of
+// the compilation instance, the PreprocessorTracker's
+// handleNewPreprocessorExit function handles cleaning up with respect
+// to the preprocessing instance.
+//
+// The PreprocessorCallbacks object uses an overidden FileChanged callback
+// to determine when a header is entered and exited (including exiting the
+// header during #include directives). It calls PreprocessorTracker's
+// handleHeaderEntry and handleHeaderExit functions upon entering and
+// exiting a header. These functions manage a stack of header handles
+// representing by a vector, pushing and popping header handles as headers
+// are entered and exited. When a HeaderInclusionPath object is created,
+// it simply copies this stack.
+//
+// The PreprocessorCallbacks object uses an overridden MacroExpands callback
+// to track when a macro expansion is performed. It calls a couple of helper
+// functions to get the unexpanded and expanded macro values as strings, but
+// then calls PreprocessorTrackerImpl's addMacroExpansionInstance function to
+// do the rest of the work. The getMacroExpandedString function uses the
+// preprocessor's getSpelling to convert tokens to strings using the
+// information passed to the MacroExpands callback, and simply concatenates
+// them. It makes recursive calls to itself to handle nested macro
+// definitions, and also handles function-style macros.
+//
+// PreprocessorTrackerImpl's addMacroExpansionInstance function looks for
+// an existing MacroExpansionTracker entry in its map of MacroExampleTracker
+// objects. If none exists, it adds one with one MacroExpansionInstance and
+// returns. If a MacroExpansionTracker object already exists, it looks for
+// an existing MacroExpansionInstance object stored in the
+// MacroExpansionTracker object, one that matches the macro expanded value
+// and the macro definition location. If a matching MacroExpansionInstance
+// object is found, it just adds the current HeaderInclusionPath object to
+// it. If not found, it creates and stores a new MacroExpantionInstance
+// object. The addMacroExpansionInstance function calls a couple of helper
+// functions to get the pre-formatted location and source line strings for
+// the macro reference and the macro definition stored as string handles.
+// These helper functions use the current source manager from the
+// preprocessor. This is done in advance at this point in time because the
+// source manager doesn't exist at the time of the reporting.
+//
+// For conditional check, the PreprocessorCallbacks class overrides the
+// PPCallbacks handlers for #if, #elif, #ifdef, and #ifndef. These handlers
+// call the addConditionalExpansionInstance method of
+// PreprocessorTrackerImpl. The process is similar to that of macros, but
+// with some different data and error messages. A lookup is performed for
+// the conditional, and if a ConditionalTracker object doesn't yet exist for
+// the conditional, a new one is added, including adding a
+// ConditionalExpansionInstance object to it to represent the condition
+// expression state. If a ConditionalTracker for the conditional does
+// exist, a lookup is made for a ConditionalExpansionInstance object
+// matching the condition expression state. If one exists, a
+// HeaderInclusionPath is added to it. Otherwise a new
+// ConditionalExpansionInstance entry is made. If a ConditionalTracker
+// has two ConditionalExpansionInstance objects, it means there was a
+// conflict, meaning the conditional expression evaluated differently in
+// one or more cases.
+//
+// After modularize has performed all the compilations, it enters a phase
+// of error reporting. This new feature adds to this reporting phase calls
+// to the PreprocessorTracker's reportInconsistentMacros and
+// reportInconsistentConditionals functions. These functions walk the maps
+// of MacroExpansionTracker's and ConditionalTracker's respectively. If
+// any of these objects have more than one MacroExpansionInstance or
+// ConditionalExpansionInstance objects, it formats and outputs an error
+// message like the example shown previously, using the stored data.
+//
+// A potential issue is that there is some overlap between the #if/#elif
+// conditional and macro reporting. I could disable the #if and #elif,
+// leaving just the #ifdef and #ifndef, since these don't overlap. Or,
+// to make clearer the separate reporting phases, I could add an output
+// message marking the phases.
+//
+// Design and Implementation Details ('Extern "C/C++" {}' Or
+// 'namespace {}') With Nested '#include' Checking)
+//
+// We override the InclusionDirective in PPCallbacks to record information
+// about each '#include' directive encountered during preprocessing.
+// We co-opt the PPItemKey class to store the information about each
+// '#include' directive, including the source file name containing the
+// directive, the name of the file being included, and the source line
+// and column of the directive. We store these object in a vector,
+// after first check to see if an entry already exists.
+//
+// Later, while the AST is being walked for other checks, we provide
+// visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}'
+// blocks, checking to see if any '#include' directives occurred
+// within the blocks, reporting errors if any found.
+//
+// Future Directions
+//
+// We probably should add options to disable any of the checks, in case
+// there is some problem with them, or the messages get too verbose.
+//
+// With the map of all the macro and conditional expansion instances,
+// it might be possible to add to the existing modularize error messages
+// (the second part referring to definitions being different), attempting
+// to tie them to the last macro conflict encountered with respect to the
+// order of the code encountered.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/Lex/LexDiagnostic.h"
+#include "PreprocessorTracker.h"
+#include "clang/Lex/MacroArgs.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/StringPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include "ModularizeUtilities.h"
+
+namespace Modularize {
+
+// Some handle types
+typedef llvm::PooledStringPtr StringHandle;
+
+typedef int HeaderHandle;
+const HeaderHandle HeaderHandleInvalid = -1;
+
+typedef int InclusionPathHandle;
+const InclusionPathHandle InclusionPathHandleInvalid = -1;
+
+// Some utility functions.
+
+// Get a "file:line:column" source location string.
+static std::string getSourceLocationString(clang::Preprocessor &PP,
+ clang::SourceLocation Loc) {
+ if (Loc.isInvalid())
+ return std::string("(none)");
+ else
+ return Loc.printToString(PP.getSourceManager());
+}
+
+// Get just the file name from a source location.
+static std::string getSourceLocationFile(clang::Preprocessor &PP,
+ clang::SourceLocation Loc) {
+ std::string Source(getSourceLocationString(PP, Loc));
+ size_t Offset = Source.find(':', 2);
+ if (Offset == std::string::npos)
+ return Source;
+ return Source.substr(0, Offset);
+}
+
+// Get just the line and column from a source location.
+static void getSourceLocationLineAndColumn(clang::Preprocessor &PP,
+ clang::SourceLocation Loc, int &Line,
+ int &Column) {
+ clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
+ if (PLoc.isInvalid()) {
+ Line = 0;
+ Column = 0;
+ return;
+ }
+ Line = PLoc.getLine();
+ Column = PLoc.getColumn();
+}
+
+// Retrieve source snippet from file image.
+static std::string getSourceString(clang::Preprocessor &PP,
+ clang::SourceRange Range) {
+ clang::SourceLocation BeginLoc = Range.getBegin();
+ clang::SourceLocation EndLoc = Range.getEnd();
+ const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
+ const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc);
+ size_t Length = EndPtr - BeginPtr;
+ return llvm::StringRef(BeginPtr, Length).trim().str();
+}
+
+// Retrieve source line from file image given a location.
+static std::string getSourceLine(clang::Preprocessor &PP,
+ clang::SourceLocation Loc) {
+ const llvm::MemoryBuffer *MemBuffer =
+ PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc));
+ const char *Buffer = MemBuffer->getBufferStart();
+ const char *BufferEnd = MemBuffer->getBufferEnd();
+ const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc);
+ const char *EndPtr = BeginPtr;
+ while (BeginPtr > Buffer) {
+ if (*BeginPtr == '\n') {
+ BeginPtr++;
+ break;
+ }
+ BeginPtr--;
+ }
+ while (EndPtr < BufferEnd) {
+ if (*EndPtr == '\n') {
+ break;
+ }
+ EndPtr++;
+ }
+ size_t Length = EndPtr - BeginPtr;
+ return llvm::StringRef(BeginPtr, Length).str();
+}
+
+// Retrieve source line from file image given a file ID and line number.
+static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,
+ int Line) {
+ const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(FileID);
+ const char *Buffer = MemBuffer->getBufferStart();
+ const char *BufferEnd = MemBuffer->getBufferEnd();
+ const char *BeginPtr = Buffer;
+ const char *EndPtr = BufferEnd;
+ int LineCounter = 1;
+ if (Line == 1)
+ BeginPtr = Buffer;
+ else {
+ while (Buffer < BufferEnd) {
+ if (*Buffer == '\n') {
+ if (++LineCounter == Line) {
+ BeginPtr = Buffer++ + 1;
+ break;
+ }
+ }
+ Buffer++;
+ }
+ }
+ while (Buffer < BufferEnd) {
+ if (*Buffer == '\n') {
+ EndPtr = Buffer;
+ break;
+ }
+ Buffer++;
+ }
+ size_t Length = EndPtr - BeginPtr;
+ return llvm::StringRef(BeginPtr, Length).str();
+}
+
+// Get the string for the Unexpanded macro instance.
+// The soureRange is expected to end at the last token
+// for the macro instance, which in the case of a function-style
+// macro will be a ')', but for an object-style macro, it
+// will be the macro name itself.
+static std::string getMacroUnexpandedString(clang::SourceRange Range,
+ clang::Preprocessor &PP,
+ llvm::StringRef MacroName,
+ const clang::MacroInfo *MI) {
+ clang::SourceLocation BeginLoc(Range.getBegin());
+ const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
+ size_t Length;
+ std::string Unexpanded;
+ if (MI->isFunctionLike()) {
+ clang::SourceLocation EndLoc(Range.getEnd());
+ const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1;
+ Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width.
+ } else
+ Length = MacroName.size();
+ return llvm::StringRef(BeginPtr, Length).trim().str();
+}
+
+// Get the expansion for a macro instance, given the information
+// provided by PPCallbacks.
+// FIXME: This doesn't support function-style macro instances
+// passed as arguments to another function-style macro. However,
+// since it still expands the inner arguments, it still
+// allows modularize to effectively work with respect to macro
+// consistency checking, although it displays the incorrect
+// expansion in error messages.
+static std::string getMacroExpandedString(clang::Preprocessor &PP,
+ llvm::StringRef MacroName,
+ const clang::MacroInfo *MI,
+ const clang::MacroArgs *Args) {
+ std::string Expanded;
+ // Walk over the macro Tokens.
+ for (const auto &T : MI->tokens()) {
+ clang::IdentifierInfo *II = T.getIdentifierInfo();
+ int ArgNo = (II && Args ? MI->getParameterNum(II) : -1);
+ if (ArgNo == -1) {
+ // This isn't an argument, just add it.
+ if (II == nullptr)
+ Expanded += PP.getSpelling(T); // Not an identifier.
+ else {
+ // Token is for an identifier.
+ std::string Name = II->getName().str();
+ // Check for nexted macro references.
+ clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
+ if (MacroInfo && (Name != MacroName))
+ Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
+ else
+ Expanded += Name;
+ }
+ continue;
+ }
+ // We get here if it's a function-style macro with arguments.
+ const clang::Token *ResultArgToks;
+ const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo);
+ if (Args->ArgNeedsPreexpansion(ArgTok, PP))
+ ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))
+ ->getPreExpArgument(ArgNo, MI, PP)[0];
+ else
+ ResultArgToks = ArgTok; // Use non-preexpanded Tokens.
+ // If the arg token didn't expand into anything, ignore it.
+ if (ResultArgToks->is(clang::tok::eof))
+ continue;
+ unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks);
+ // Append the resulting argument expansions.
+ for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) {
+ const clang::Token &AT = ResultArgToks[ArgumentIndex];
+ clang::IdentifierInfo *II = AT.getIdentifierInfo();
+ if (II == nullptr)
+ Expanded += PP.getSpelling(AT); // Not an identifier.
+ else {
+ // It's an identifier. Check for further expansion.
+ std::string Name = II->getName().str();
+ clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
+ if (MacroInfo)
+ Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
+ else
+ Expanded += Name;
+ }
+ }
+ }
+ return Expanded;
+}
+
+namespace {
+
+// ConditionValueKind strings.
+const char *
+ConditionValueKindStrings[] = {
+ "(not evaluated)", "false", "true"
+};
+
+bool operator<(const StringHandle &H1, const StringHandle &H2) {
+ const char *S1 = (H1 ? *H1 : "");
+ const char *S2 = (H2 ? *H2 : "");
+ int Diff = strcmp(S1, S2);
+ return Diff < 0;
+}
+bool operator>(const StringHandle &H1, const StringHandle &H2) {
+ const char *S1 = (H1 ? *H1 : "");
+ const char *S2 = (H2 ? *H2 : "");
+ int Diff = strcmp(S1, S2);
+ return Diff > 0;
+}
+
+// Preprocessor item key.
+//
+// This class represents a location in a source file, for use
+// as a key representing a unique name/file/line/column quadruplet,
+// which in this case is used to identify a macro expansion instance,
+// but could be used for other things as well.
+// The file is a header file handle, the line is a line number,
+// and the column is a column number.
+class PPItemKey {
+public:
+ PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,
+ clang::SourceLocation Loc)
+ : Name(Name), File(File) {
+ getSourceLocationLineAndColumn(PP, Loc, Line, Column);
+ }
+ PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
+ : Name(Name), File(File), Line(Line), Column(Column) {}
+ PPItemKey(const PPItemKey &Other)
+ : Name(Other.Name), File(Other.File), Line(Other.Line),
+ Column(Other.Column) {}
+ PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
+ bool operator==(const PPItemKey &Other) const {
+ if (Name != Other.Name)
+ return false;
+ if (File != Other.File)
+ return false;
+ if (Line != Other.Line)
+ return false;
+ return Column == Other.Column;
+ }
+ bool operator<(const PPItemKey &Other) const {
+ if (Name < Other.Name)
+ return true;
+ else if (Name > Other.Name)
+ return false;
+ if (File < Other.File)
+ return true;
+ else if (File > Other.File)
+ return false;
+ if (Line < Other.Line)
+ return true;
+ else if (Line > Other.Line)
+ return false;
+ return Column < Other.Column;
+ }
+ StringHandle Name;
+ HeaderHandle File;
+ int Line;
+ int Column;
+};
+
+// Header inclusion path.
+class HeaderInclusionPath {
+public:
+ HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
+ : Path(HeaderInclusionPath) {}
+ HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
+ HeaderInclusionPath() {}
+ std::vector<HeaderHandle> Path;
+};
+
+// Macro expansion instance.
+//
+// This class represents an instance of a macro expansion with a
+// unique value. It also stores the unique header inclusion paths
+// for use in telling the user the nested include path to the header.
+class MacroExpansionInstance {
+public:
+ MacroExpansionInstance(StringHandle MacroExpanded,
+ PPItemKey &DefinitionLocation,
+ StringHandle DefinitionSourceLine,
+ InclusionPathHandle H)
+ : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
+ DefinitionSourceLine(DefinitionSourceLine) {
+ InclusionPathHandles.push_back(H);
+ }
+ MacroExpansionInstance() {}
+
+ // Check for the presence of a header inclusion path handle entry.
+ // Return false if not found.
+ bool haveInclusionPathHandle(InclusionPathHandle H) {
+ for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
+ I != E; ++I) {
+ if (*I == H)
+ return true;
+ }
+ return InclusionPathHandleInvalid;
+ }
+ // Add a new header inclusion path entry, if not already present.
+ void addInclusionPathHandle(InclusionPathHandle H) {
+ if (!haveInclusionPathHandle(H))
+ InclusionPathHandles.push_back(H);
+ }
+
+ // A string representing the macro instance after preprocessing.
+ StringHandle MacroExpanded;
+ // A file/line/column triplet representing the macro definition location.
+ PPItemKey DefinitionLocation;
+ // A place to save the macro definition line string.
+ StringHandle DefinitionSourceLine;
+ // The header inclusion path handles for all the instances.
+ std::vector<InclusionPathHandle> InclusionPathHandles;
+};
+
+// Macro expansion instance tracker.
+//
+// This class represents one macro expansion, keyed by a PPItemKey.
+// It stores a string representing the macro reference in the source,
+// and a list of ConditionalExpansionInstances objects representing
+// the unique values the condition expands to in instances of the header.
+class MacroExpansionTracker {
+public:
+ MacroExpansionTracker(StringHandle MacroUnexpanded,
+ StringHandle MacroExpanded,
+ StringHandle InstanceSourceLine,
+ PPItemKey &DefinitionLocation,
+ StringHandle DefinitionSourceLine,
+ InclusionPathHandle InclusionPathHandle)
+ : MacroUnexpanded(MacroUnexpanded),
+ InstanceSourceLine(InstanceSourceLine) {
+ addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
+ DefinitionSourceLine, InclusionPathHandle);
+ }
+ MacroExpansionTracker() {}
+
+ // Find a matching macro expansion instance.
+ MacroExpansionInstance *
+ findMacroExpansionInstance(StringHandle MacroExpanded,
+ PPItemKey &DefinitionLocation) {
+ for (auto I = MacroExpansionInstances.begin(),
+ E = MacroExpansionInstances.end();
+ I != E; ++I) {
+ if ((I->MacroExpanded == MacroExpanded) &&
+ (I->DefinitionLocation == DefinitionLocation)) {
+ return &*I; // Found.
+ }
+ }
+ return nullptr; // Not found.
+ }
+
+ // Add a macro expansion instance.
+ void addMacroExpansionInstance(StringHandle MacroExpanded,
+ PPItemKey &DefinitionLocation,
+ StringHandle DefinitionSourceLine,
+ InclusionPathHandle InclusionPathHandle) {
+ MacroExpansionInstances.push_back(
+ MacroExpansionInstance(MacroExpanded, DefinitionLocation,
+ DefinitionSourceLine, InclusionPathHandle));
+ }
+
+ // Return true if there is a mismatch.
+ bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
+
+ // A string representing the macro instance without expansion.
+ StringHandle MacroUnexpanded;
+ // A place to save the macro instance source line string.
+ StringHandle InstanceSourceLine;
+ // The macro expansion instances.
+ // If all instances of the macro expansion expand to the same value,
+ // This vector will only have one instance.
+ std::vector<MacroExpansionInstance> MacroExpansionInstances;
+};
+
+// Conditional expansion instance.
+//
+// This class represents an instance of a condition exoression result
+// with a unique value. It also stores the unique header inclusion paths
+// for use in telling the user the nested include path to the header.
+class ConditionalExpansionInstance {
+public:
+ ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
+ : ConditionValue(ConditionValue) {
+ InclusionPathHandles.push_back(H);
+ }
+ ConditionalExpansionInstance() {}
+
+ // Check for the presence of a header inclusion path handle entry.
+ // Return false if not found.
+ bool haveInclusionPathHandle(InclusionPathHandle H) {
+ for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
+ I != E; ++I) {
+ if (*I == H)
+ return true;
+ }
+ return InclusionPathHandleInvalid;
+ }
+ // Add a new header inclusion path entry, if not already present.
+ void addInclusionPathHandle(InclusionPathHandle H) {
+ if (!haveInclusionPathHandle(H))
+ InclusionPathHandles.push_back(H);
+ }
+
+ // A flag representing the evaluated condition value.
+ clang::PPCallbacks::ConditionValueKind ConditionValue;
+ // The header inclusion path handles for all the instances.
+ std::vector<InclusionPathHandle> InclusionPathHandles;
+};
+
+// Conditional directive instance tracker.
+//
+// This class represents one conditional directive, keyed by a PPItemKey.
+// It stores a string representing the macro reference in the source,
+// and a list of ConditionExpansionInstance objects representing
+// the unique value the condition expression expands to in instances of
+// the header.
+class ConditionalTracker {
+public:
+ ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
+ clang::PPCallbacks::ConditionValueKind ConditionValue,
+ StringHandle ConditionUnexpanded,
+ InclusionPathHandle InclusionPathHandle)
+ : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
+ addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
+ }
+ ConditionalTracker() {}
+
+ // Find a matching condition expansion instance.
+ ConditionalExpansionInstance *
+ findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
+ for (auto I = ConditionalExpansionInstances.begin(),
+ E = ConditionalExpansionInstances.end();
+ I != E; ++I) {
+ if (I->ConditionValue == ConditionValue) {
+ return &*I; // Found.
+ }
+ }
+ return nullptr; // Not found.
+ }
+
+ // Add a conditional expansion instance.
+ void
+ addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
+ InclusionPathHandle InclusionPathHandle) {
+ ConditionalExpansionInstances.push_back(
+ ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
+ }
+
+ // Return true if there is a mismatch.
+ bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
+
+ // The kind of directive.
+ clang::tok::PPKeywordKind DirectiveKind;
+ // A string representing the macro instance without expansion.
+ StringHandle ConditionUnexpanded;
+ // The condition expansion instances.
+ // If all instances of the conditional expression expand to the same value,
+ // This vector will only have one instance.
+ std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
+};
+
+class PreprocessorTrackerImpl;
+
+// Preprocessor callbacks for modularize.
+//
+// This class derives from the Clang PPCallbacks class to track preprocessor
+// actions, such as changing files and handling preprocessor directives and
+// macro expansions. It has to figure out when a new header file is entered
+// and left, as the provided handler is not particularly clear about it.
+class PreprocessorCallbacks : public clang::PPCallbacks {
+public:
+ PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
+ clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
+ : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
+ ~PreprocessorCallbacks() override {}
+
+ // Overridden handlers.
+ void InclusionDirective(clang::SourceLocation HashLoc,
+ const clang::Token &IncludeTok,
+ llvm::StringRef FileName, bool IsAngled,
+ clang::CharSourceRange FilenameRange,
+ const clang::FileEntry *File,
+ llvm::StringRef SearchPath,
+ llvm::StringRef RelativePath,
+ const clang::Module *Imported) override;
+ void FileChanged(clang::SourceLocation Loc,
+ clang::PPCallbacks::FileChangeReason Reason,
+ clang::SrcMgr::CharacteristicKind FileType,
+ clang::FileID PrevFID = clang::FileID()) override;
+ void MacroExpands(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD, clang::SourceRange Range,
+ const clang::MacroArgs *Args) override;
+ void Defined(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD,
+ clang::SourceRange Range) override;
+ void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+ clang::PPCallbacks::ConditionValueKind ConditionResult) override;
+ void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+ clang::PPCallbacks::ConditionValueKind ConditionResult,
+ clang::SourceLocation IfLoc) override;
+ void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) override;
+ void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) override;
+
+private:
+ PreprocessorTrackerImpl &PPTracker;
+ clang::Preprocessor &PP;
+ std::string RootHeaderFile;
+};
+
+// Preprocessor macro expansion item map types.
+typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;
+typedef std::map<PPItemKey, MacroExpansionTracker>::iterator
+MacroExpansionMapIter;
+
+// Preprocessor conditional expansion item map types.
+typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;
+typedef std::map<PPItemKey, ConditionalTracker>::iterator
+ConditionalExpansionMapIter;
+
+// Preprocessor tracker for modularize.
+//
+// This class stores information about all the headers processed in the
+// course of running modularize.
+class PreprocessorTrackerImpl : public PreprocessorTracker {
+public:
+ PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,
+ bool DoBlockCheckHeaderListOnly)
+ : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),
+ CurrentInclusionPathHandle(InclusionPathHandleInvalid),
+ InNestedHeader(false) {
+ // Use canonical header path representation.
+ for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),
+ E = Headers.end();
+ I != E; ++I) {
+ HeaderList.push_back(getCanonicalPath(*I));
+ }
+ }
+
+ ~PreprocessorTrackerImpl() override {}
+
+ // Handle entering a preprocessing session.
+ void handlePreprocessorEntry(clang::Preprocessor &PP,
+ llvm::StringRef rootHeaderFile) override {
+ HeadersInThisCompile.clear();
+ assert((HeaderStack.size() == 0) && "Header stack should be empty.");
+ pushHeaderHandle(addHeader(rootHeaderFile));
+ PP.addPPCallbacks(llvm::make_unique<PreprocessorCallbacks>(*this, PP,
+ rootHeaderFile));
+ }
+ // Handle exiting a preprocessing session.
+ void handlePreprocessorExit() override { HeaderStack.clear(); }
+
+ // Handle include directive.
+ // This function is called every time an include directive is seen by the
+ // preprocessor, for the purpose of later checking for 'extern "" {}' or
+ // "namespace {}" blocks containing #include directives.
+ void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,
+ int DirectiveColumn,
+ llvm::StringRef TargetPath) override {
+ // If it's not a header in the header list, ignore it with respect to
+ // the check.
+ if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath))
+ return;
+ HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath);
+ StringHandle IncludeHeaderHandle = addString(TargetPath);
+ for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
+ E = IncludeDirectives.end();
+ I != E; ++I) {
+ // If we already have an entry for this directive, return now.
+ if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))
+ return;
+ }
+ PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,
+ DirectiveLine, DirectiveColumn);
+ IncludeDirectives.push_back(IncludeDirectiveItem);
+ }
+
+ // Check for include directives within the given source line range.
+ // Report errors if any found. Returns true if no include directives
+ // found in block.
+ bool checkForIncludesInBlock(clang::Preprocessor &PP,
+ clang::SourceRange BlockSourceRange,
+ const char *BlockIdentifierMessage,
+ llvm::raw_ostream &OS) override {
+ clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();
+ clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();
+ // Use block location to get FileID of both the include directive
+ // and block statement.
+ clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc);
+ std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc);
+ SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath);
+ HeaderHandle SourceHandle = findHeaderHandle(SourcePath);
+ if (SourceHandle == -1)
+ return true;
+ int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;
+ bool returnValue = true;
+ getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine,
+ BlockStartColumn);
+ getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine,
+ BlockEndColumn);
+ for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
+ E = IncludeDirectives.end();
+ I != E; ++I) {
+ // If we find an entry within the block, report an error.
+ if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&
+ (I->Line < BlockEndLine)) {
+ returnValue = false;
+ OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";
+ OS << getSourceLine(PP, FileID, I->Line) << "\n";
+ if (I->Column > 0)
+ OS << std::string(I->Column - 1, ' ') << "^\n";
+ OS << "error: Include directive within " << BlockIdentifierMessage
+ << ".\n";
+ OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn
+ << ":\n";
+ OS << getSourceLine(PP, BlockStartLoc) << "\n";
+ if (BlockStartColumn > 0)
+ OS << std::string(BlockStartColumn - 1, ' ') << "^\n";
+ OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";
+ }
+ }
+ return returnValue;
+ }
+
+ // Handle entering a header source file.
+ void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {
+ // Ignore <built-in> and <command-line> to reduce message clutter.
+ if (HeaderPath.startswith("<"))
+ return;
+ HeaderHandle H = addHeader(HeaderPath);
+ if (H != getCurrentHeaderHandle())
+ pushHeaderHandle(H);
+ // Check for nested header.
+ if (!InNestedHeader)
+ InNestedHeader = !HeadersInThisCompile.insert(H).second;
+ }
+
+ // Handle exiting a header source file.
+ void handleHeaderExit(llvm::StringRef HeaderPath) {
+ // Ignore <built-in> and <command-line> to reduce message clutter.
+ if (HeaderPath.startswith("<"))
+ return;
+ HeaderHandle H = findHeaderHandle(HeaderPath);
+ HeaderHandle TH;
+ if (isHeaderHandleInStack(H)) {
+ do {
+ TH = getCurrentHeaderHandle();
+ popHeaderHandle();
+ } while ((TH != H) && (HeaderStack.size() != 0));
+ }
+ InNestedHeader = false;
+ }
+
+ // Lookup/add string.
+ StringHandle addString(llvm::StringRef Str) { return Strings.intern(Str); }
+
+ // Convert to a canonical path.
+ std::string getCanonicalPath(llvm::StringRef path) const {
+ std::string CanonicalPath(path);
+ std::replace(CanonicalPath.begin(), CanonicalPath.end(), '\\', '/');
+ return CanonicalPath;
+ }
+
+ // Return true if the given header is in the header list.
+ bool isHeaderListHeader(llvm::StringRef HeaderPath) const {
+ std::string CanonicalPath = getCanonicalPath(HeaderPath);
+ for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),
+ E = HeaderList.end();
+ I != E; ++I) {
+ if (*I == CanonicalPath)
+ return true;
+ }
+ return false;
+ }
+
+ // Get the handle of a header file entry.
+ // Return HeaderHandleInvalid if not found.
+ HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {
+ std::string CanonicalPath = getCanonicalPath(HeaderPath);
+ HeaderHandle H = 0;
+ for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;
+ ++I, ++H) {
+ if (**I == CanonicalPath)
+ return H;
+ }
+ return HeaderHandleInvalid;
+ }
+
+ // Add a new header file entry, or return existing handle.
+ // Return the header handle.
+ HeaderHandle addHeader(llvm::StringRef HeaderPath) {
+ std::string CanonicalPath = getCanonicalPath(HeaderPath);
+ HeaderHandle H = findHeaderHandle(CanonicalPath);
+ if (H == HeaderHandleInvalid) {
+ H = HeaderPaths.size();
+ HeaderPaths.push_back(addString(CanonicalPath));
+ }
+ return H;
+ }
+
+ // Return a header file path string given its handle.
+ StringHandle getHeaderFilePath(HeaderHandle H) const {
+ if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))
+ return HeaderPaths[H];
+ return StringHandle();
+ }
+
+ // Returns a handle to the inclusion path.
+ InclusionPathHandle pushHeaderHandle(HeaderHandle H) {
+ HeaderStack.push_back(H);
+ return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
+ }
+ // Pops the last header handle from the stack;
+ void popHeaderHandle() {
+ // assert((HeaderStack.size() != 0) && "Header stack already empty.");
+ if (HeaderStack.size() != 0) {
+ HeaderStack.pop_back();
+ CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
+ }
+ }
+ // Get the top handle on the header stack.
+ HeaderHandle getCurrentHeaderHandle() const {
+ if (HeaderStack.size() != 0)
+ return HeaderStack.back();
+ return HeaderHandleInvalid;
+ }
+
+ // Check for presence of header handle in the header stack.
+ bool isHeaderHandleInStack(HeaderHandle H) const {
+ for (auto I = HeaderStack.begin(), E = HeaderStack.end(); I != E; ++I) {
+ if (*I == H)
+ return true;
+ }
+ return false;
+ }
+
+ // Get the handle of a header inclusion path entry.
+ // Return InclusionPathHandleInvalid if not found.
+ InclusionPathHandle
+ findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
+ InclusionPathHandle H = 0;
+ for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
+ ++I, ++H) {
+ if (I->Path == Path)
+ return H;
+ }
+ return HeaderHandleInvalid;
+ }
+ // Add a new header inclusion path entry, or return existing handle.
+ // Return the header inclusion path entry handle.
+ InclusionPathHandle
+ addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
+ InclusionPathHandle H = findInclusionPathHandle(Path);
+ if (H == HeaderHandleInvalid) {
+ H = InclusionPaths.size();
+ InclusionPaths.push_back(HeaderInclusionPath(Path));
+ }
+ return H;
+ }
+ // Return the current inclusion path handle.
+ InclusionPathHandle getCurrentInclusionPathHandle() const {
+ return CurrentInclusionPathHandle;
+ }
+
+ // Return an inclusion path given its handle.
+ const std::vector<HeaderHandle> &
+ getInclusionPath(InclusionPathHandle H) const {
+ if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
+ return InclusionPaths[H].Path;
+ static std::vector<HeaderHandle> Empty;
+ return Empty;
+ }
+
+ // Add a macro expansion instance.
+ void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
+ clang::SourceLocation InstanceLoc,
+ clang::SourceLocation DefinitionLoc,
+ clang::IdentifierInfo *II,
+ llvm::StringRef MacroUnexpanded,
+ llvm::StringRef MacroExpanded,
+ InclusionPathHandle InclusionPathHandle) {
+ if (InNestedHeader)
+ return;
+ StringHandle MacroName = addString(II->getName());
+ PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
+ PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
+ auto I = MacroExpansions.find(InstanceKey);
+ // If existing instance of expansion not found, add one.
+ if (I == MacroExpansions.end()) {
+ std::string InstanceSourceLine =
+ getSourceLocationString(PP, InstanceLoc) + ":\n" +
+ getSourceLine(PP, InstanceLoc) + "\n";
+ std::string DefinitionSourceLine =
+ getSourceLocationString(PP, DefinitionLoc) + ":\n" +
+ getSourceLine(PP, DefinitionLoc) + "\n";
+ MacroExpansions[InstanceKey] = MacroExpansionTracker(
+ addString(MacroUnexpanded), addString(MacroExpanded),
+ addString(InstanceSourceLine), DefinitionKey,
+ addString(DefinitionSourceLine), InclusionPathHandle);
+ } else {
+ // We've seen the macro before. Get its tracker.
+ MacroExpansionTracker &CondTracker = I->second;
+ // Look up an existing instance value for the macro.
+ MacroExpansionInstance *MacroInfo =
+ CondTracker.findMacroExpansionInstance(addString(MacroExpanded),
+ DefinitionKey);
+ // If found, just add the inclusion path to the instance.
+ if (MacroInfo)
+ MacroInfo->addInclusionPathHandle(InclusionPathHandle);
+ else {
+ // Otherwise add a new instance with the unique value.
+ std::string DefinitionSourceLine =
+ getSourceLocationString(PP, DefinitionLoc) + ":\n" +
+ getSourceLine(PP, DefinitionLoc) + "\n";
+ CondTracker.addMacroExpansionInstance(
+ addString(MacroExpanded), DefinitionKey,
+ addString(DefinitionSourceLine), InclusionPathHandle);
+ }
+ }
+ }
+
+ // Add a conditional expansion instance.
+ void
+ addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
+ clang::SourceLocation InstanceLoc,
+ clang::tok::PPKeywordKind DirectiveKind,
+ clang::PPCallbacks::ConditionValueKind ConditionValue,
+ llvm::StringRef ConditionUnexpanded,
+ InclusionPathHandle InclusionPathHandle) {
+ // Ignore header guards, assuming the header guard is the only conditional.
+ if (InNestedHeader)
+ return;
+ StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));
+ PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
+ auto I = ConditionalExpansions.find(InstanceKey);
+ // If existing instance of condition not found, add one.
+ if (I == ConditionalExpansions.end()) {
+ std::string InstanceSourceLine =
+ getSourceLocationString(PP, InstanceLoc) + ":\n" +
+ getSourceLine(PP, InstanceLoc) + "\n";
+ ConditionalExpansions[InstanceKey] =
+ ConditionalTracker(DirectiveKind, ConditionValue,
+ ConditionUnexpandedHandle, InclusionPathHandle);
+ } else {
+ // We've seen the conditional before. Get its tracker.
+ ConditionalTracker &CondTracker = I->second;
+ // Look up an existing instance value for the condition.
+ ConditionalExpansionInstance *MacroInfo =
+ CondTracker.findConditionalExpansionInstance(ConditionValue);
+ // If found, just add the inclusion path to the instance.
+ if (MacroInfo)
+ MacroInfo->addInclusionPathHandle(InclusionPathHandle);
+ else {
+ // Otherwise add a new instance with the unique value.
+ CondTracker.addConditionalExpansionInstance(ConditionValue,
+ InclusionPathHandle);
+ }
+ }
+ }
+
+ // Report on inconsistent macro instances.
+ // Returns true if any mismatches.
+ bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
+ bool ReturnValue = false;
+ // Walk all the macro expansion trackers in the map.
+ for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
+ ++I) {
+ const PPItemKey &ItemKey = I->first;
+ MacroExpansionTracker &MacroExpTracker = I->second;
+ // If no mismatch (only one instance value) continue.
+ if (!MacroExpTracker.hasMismatch())
+ continue;
+ // Tell caller we found one or more errors.
+ ReturnValue = true;
+ // Start the error message.
+ OS << *MacroExpTracker.InstanceSourceLine;
+ if (ItemKey.Column > 0)
+ OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
+ OS << "error: Macro instance '" << *MacroExpTracker.MacroUnexpanded
+ << "' has different values in this header, depending on how it was "
+ "included.\n";
+ // Walk all the instances.
+ for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
+ EMT = MacroExpTracker.MacroExpansionInstances.end();
+ IMT != EMT; ++IMT) {
+ MacroExpansionInstance &MacroInfo = *IMT;
+ OS << " '" << *MacroExpTracker.MacroUnexpanded << "' expanded to: '"
+ << *MacroInfo.MacroExpanded
+ << "' with respect to these inclusion paths:\n";
+ // Walk all the inclusion path hierarchies.
+ for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
+ EIP = MacroInfo.InclusionPathHandles.end();
+ IIP != EIP; ++IIP) {
+ const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+ auto Count = (int)ip.size();
+ for (int Index = 0; Index < Count; ++Index) {
+ HeaderHandle H = ip[Index];
+ OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H)
+ << "\n";
+ }
+ }
+ // For a macro that wasn't defined, we flag it by using the
+ // instance location.
+ // If there is a definition...
+ if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
+ OS << *MacroInfo.DefinitionSourceLine;
+ if (MacroInfo.DefinitionLocation.Column > 0)
+ OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
+ << "^\n";
+ OS << "Macro defined here.\n";
+ } else
+ OS << "(no macro definition)"
+ << "\n";
+ }
+ }
+ return ReturnValue;
+ }
+
+ // Report on inconsistent conditional instances.
+ // Returns true if any mismatches.
+ bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
+ bool ReturnValue = false;
+ // Walk all the conditional trackers in the map.
+ for (auto I = ConditionalExpansions.begin(),
+ E = ConditionalExpansions.end();
+ I != E; ++I) {
+ const PPItemKey &ItemKey = I->first;
+ ConditionalTracker &CondTracker = I->second;
+ if (!CondTracker.hasMismatch())
+ continue;
+ // Tell caller we found one or more errors.
+ ReturnValue = true;
+ // Start the error message.
+ OS << *HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
+ << ItemKey.Column << "\n";
+ OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "
+ << *CondTracker.ConditionUnexpanded << "\n";
+ OS << "^\n";
+ OS << "error: Conditional expression instance '"
+ << *CondTracker.ConditionUnexpanded
+ << "' has different values in this header, depending on how it was "
+ "included.\n";
+ // Walk all the instances.
+ for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
+ EMT = CondTracker.ConditionalExpansionInstances.end();
+ IMT != EMT; ++IMT) {
+ ConditionalExpansionInstance &MacroInfo = *IMT;
+ OS << " '" << *CondTracker.ConditionUnexpanded << "' expanded to: '"
+ << ConditionValueKindStrings[MacroInfo.ConditionValue]
+ << "' with respect to these inclusion paths:\n";
+ // Walk all the inclusion path hierarchies.
+ for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
+ EIP = MacroInfo.InclusionPathHandles.end();
+ IIP != EIP; ++IIP) {
+ const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+ auto Count = (int)ip.size();
+ for (int Index = 0; Index < Count; ++Index) {
+ HeaderHandle H = ip[Index];
+ OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H)
+ << "\n";
+ }
+ }
+ }
+ }
+ return ReturnValue;
+ }
+
+ // Get directive spelling.
+ static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
+ switch (kind) {
+ case clang::tok::pp_if:
+ return "if";
+ case clang::tok::pp_elif:
+ return "elif";
+ case clang::tok::pp_ifdef:
+ return "ifdef";
+ case clang::tok::pp_ifndef:
+ return "ifndef";
+ default:
+ return "(unknown)";
+ }
+ }
+
+private:
+ llvm::SmallVector<std::string, 32> HeaderList;
+ // Only do extern, namespace check for headers in HeaderList.
+ bool BlockCheckHeaderListOnly;
+ llvm::StringPool Strings;
+ std::vector<StringHandle> HeaderPaths;
+ std::vector<HeaderHandle> HeaderStack;
+ std::vector<HeaderInclusionPath> InclusionPaths;
+ InclusionPathHandle CurrentInclusionPathHandle;
+ llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
+ std::vector<PPItemKey> IncludeDirectives;
+ MacroExpansionMap MacroExpansions;
+ ConditionalExpansionMap ConditionalExpansions;
+ bool InNestedHeader;
+};
+
+} // namespace
+
+// PreprocessorTracker functions.
+
+// PreprocessorTracker desctructor.
+PreprocessorTracker::~PreprocessorTracker() {}
+
+// Create instance of PreprocessorTracker.
+PreprocessorTracker *PreprocessorTracker::create(
+ llvm::SmallVector<std::string, 32> &Headers,
+ bool DoBlockCheckHeaderListOnly) {
+ return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
+}
+
+// Preprocessor callbacks for modularize.
+
+// Handle include directive.
+void PreprocessorCallbacks::InclusionDirective(
+ clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
+ llvm::StringRef FileName, bool IsAngled,
+ clang::CharSourceRange FilenameRange, const clang::FileEntry *File,
+ llvm::StringRef SearchPath, llvm::StringRef RelativePath,
+ const clang::Module *Imported) {
+ int DirectiveLine, DirectiveColumn;
+ std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
+ getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
+ PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,
+ FileName);
+}
+
+// Handle file entry/exit.
+void PreprocessorCallbacks::FileChanged(
+ clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
+ clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
+ switch (Reason) {
+ case EnterFile:
+ PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));
+ break;
+ case ExitFile: {
+ const clang::FileEntry *F =
+ PP.getSourceManager().getFileEntryForID(PrevFID);
+ if (F)
+ PPTracker.handleHeaderExit(F->getName());
+ } break;
+ case SystemHeaderPragma:
+ case RenameFile:
+ break;
+ }
+}
+
+// Handle macro expansion.
+void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD,
+ clang::SourceRange Range,
+ const clang::MacroArgs *Args) {
+ clang::SourceLocation Loc = Range.getBegin();
+ // Ignore macro argument expansions.
+ if (!Loc.isFileID())
+ return;
+ clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
+ const clang::MacroInfo *MI = MD.getMacroInfo();
+ std::string MacroName = II->getName().str();
+ std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
+ std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
+ PPTracker.addMacroExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,
+ Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD,
+ clang::SourceRange Range) {
+ clang::SourceLocation Loc(Range.getBegin());
+ clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
+ const clang::MacroInfo *MI = MD.getMacroInfo();
+ std::string MacroName = II->getName().str();
+ std::string Unexpanded(getSourceString(PP, Range));
+ PPTracker.addMacroExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc,
+ (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,
+ (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::If(clang::SourceLocation Loc,
+ clang::SourceRange ConditionRange,
+ clang::PPCallbacks::ConditionValueKind ConditionResult) {
+ std::string Unexpanded(getSourceString(PP, ConditionRange));
+ PPTracker.addConditionalExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,
+ ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
+ clang::SourceRange ConditionRange,
+ clang::PPCallbacks::ConditionValueKind ConditionResult,
+ clang::SourceLocation IfLoc) {
+ std::string Unexpanded(getSourceString(PP, ConditionRange));
+ PPTracker.addConditionalExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,
+ ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
+ const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) {
+ clang::PPCallbacks::ConditionValueKind IsDefined =
+ (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
+ PPTracker.addConditionalExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,
+ IsDefined, PP.getSpelling(MacroNameTok),
+ PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
+ const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) {
+ clang::PPCallbacks::ConditionValueKind IsNotDefined =
+ (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
+ PPTracker.addConditionalExpansionInstance(
+ PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,
+ IsNotDefined, PP.getSpelling(MacroNameTok),
+ PPTracker.getCurrentInclusionPathHandle());
+}
+} // end namespace Modularize
--- /dev/null
+//===- PreprocessorTracker.h - Tracks preprocessor activities -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Macro expansions and preprocessor conditional consistency checker.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZE_PREPROCESSOR_TRACKER_H
+#define MODULARIZE_PREPROCESSOR_TRACKER_H
+
+#include "clang/Lex/Preprocessor.h"
+
+namespace Modularize {
+
+/// \brief Preprocessor tracker for modularize.
+///
+/// The PreprocessorTracker class defines an API for
+/// checking macro expansions and preprocessor conditional expressions
+/// in a header file for consistency among one or more compilations of
+/// the header in a #include scenario. This is for helping a user
+/// find which macro expansions or conditionals might be problematic with
+/// respect to using the headers in the modules scenario, because they
+/// evaluate to different values depending on how or where a header
+/// is included.
+///
+/// The handlePreprocessorEntry function implementation will register
+/// a PPCallbacks object in the given Preprocessor object. The calls to
+/// the callbacks will collect information about the macro expansions
+/// and preprocessor conditionals encountered, for later analysis and
+/// reporting of inconsistencies between runs performed by calls to
+/// the reportInconsistentMacros and reportInconsistentConditionals
+/// functions respectively. The handlePreprocessorExit informs the
+/// implementation that a preprocessing session is complete, allowing
+/// it to do any needed compilation completion activities in the checker.
+class PreprocessorTracker {
+public:
+ virtual ~PreprocessorTracker();
+
+ // Handle entering a preprocessing session.
+ // (Called after a Preprocessor object is created, but before preprocessing.)
+ virtual void handlePreprocessorEntry(clang::Preprocessor &PP,
+ llvm::StringRef RootHeaderFile) = 0;
+ // Handle exiting a preprocessing session.
+ // (Called after preprocessing is complete, but before the Preprocessor
+ // object is destroyed.)
+ virtual void handlePreprocessorExit() = 0;
+
+ // Handle include directive.
+ // This function is called every time an include directive is seen by the
+ // preprocessor, for the purpose of later checking for 'extern "" {}' or
+ // "namespace {}" blocks containing #include directives.
+ virtual void handleIncludeDirective(llvm::StringRef DirectivePath,
+ int DirectiveLine, int DirectiveColumn,
+ llvm::StringRef TargetPath) = 0;
+
+ // Check for include directives within the given source line range.
+ // Report errors if any found. Returns true if no include directives
+ // found in block.
+ virtual bool checkForIncludesInBlock(clang::Preprocessor &PP,
+ clang::SourceRange BlockSourceRange,
+ const char *BlockIdentifierMessage,
+ llvm::raw_ostream &OS) = 0;
+
+ // Report on inconsistent macro instances.
+ // Returns true if any mismatches.
+ virtual bool reportInconsistentMacros(llvm::raw_ostream &OS) = 0;
+
+ // Report on inconsistent conditional directive instances.
+ // Returns true if any mismatches.
+ virtual bool reportInconsistentConditionals(llvm::raw_ostream &OS) = 0;
+
+ // Create instance of PreprocessorTracker.
+ static PreprocessorTracker *create(
+ llvm::SmallVector<std::string, 32> &Headers,
+ bool DoBlockCheckHeaderListOnly);
+};
+
+} // end namespace Modularize
+
+#endif
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_executable(pp-trace
+ PPTrace.cpp
+ PPCallbacksTracker.cpp
+ )
+
+target_link_libraries(pp-trace
+ clangAST
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTooling
+ )
--- /dev/null
+//===--- PPCallbacksTracker.cpp - Preprocessor tracker -*--*---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implementations for preprocessor tracking.
+///
+/// See the header for details.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PPCallbacksTracker.h"
+#include "clang/Lex/MacroArgs.h"
+#include "llvm/Support/raw_ostream.h"
+
+// Utility functions.
+
+// Get a "file:line:column" source location string.
+static std::string getSourceLocationString(clang::Preprocessor &PP,
+ clang::SourceLocation Loc) {
+ if (Loc.isInvalid())
+ return std::string("(none)");
+
+ if (Loc.isFileID()) {
+ clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
+
+ if (PLoc.isInvalid()) {
+ return std::string("(invalid)");
+ }
+
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+
+ // The macro expansion and spelling pos is identical for file locs.
+ SS << "\"" << PLoc.getFilename() << ':' << PLoc.getLine() << ':'
+ << PLoc.getColumn() << "\"";
+
+ std::string Result = SS.str();
+
+ // YAML treats backslash as escape, so use forward slashes.
+ std::replace(Result.begin(), Result.end(), '\\', '/');
+
+ return Result;
+ }
+
+ return std::string("(nonfile)");
+}
+
+// Enum string tables.
+
+// FileChangeReason strings.
+static const char *const FileChangeReasonStrings[] = {
+ "EnterFile", "ExitFile", "SystemHeaderPragma", "RenameFile"
+};
+
+// CharacteristicKind strings.
+static const char *const CharacteristicKindStrings[] = { "C_User", "C_System",
+ "C_ExternCSystem" };
+
+// MacroDirective::Kind strings.
+static const char *const MacroDirectiveKindStrings[] = {
+ "MD_Define","MD_Undefine", "MD_Visibility"
+};
+
+// PragmaIntroducerKind strings.
+static const char *const PragmaIntroducerKindStrings[] = { "PIK_HashPragma",
+ "PIK__Pragma",
+ "PIK___pragma" };
+
+// PragmaMessageKind strings.
+static const char *const PragmaMessageKindStrings[] = {
+ "PMK_Message", "PMK_Warning", "PMK_Error"
+};
+
+// ConditionValueKind strings.
+static const char *const ConditionValueKindStrings[] = {
+ "CVK_NotEvaluated", "CVK_False", "CVK_True"
+};
+
+// Mapping strings.
+static const char *const MappingStrings[] = { "0", "MAP_IGNORE",
+ "MAP_REMARK", "MAP_WARNING",
+ "MAP_ERROR", "MAP_FATAL" };
+
+// PPCallbacksTracker functions.
+
+PPCallbacksTracker::PPCallbacksTracker(llvm::SmallSet<std::string, 4> &Ignore,
+ std::vector<CallbackCall> &CallbackCalls,
+ clang::Preprocessor &PP)
+ : CallbackCalls(CallbackCalls), Ignore(Ignore), PP(PP) {}
+
+PPCallbacksTracker::~PPCallbacksTracker() {}
+
+// Callback functions.
+
+// Callback invoked whenever a source file is entered or exited.
+void PPCallbacksTracker::FileChanged(
+ clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
+ clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
+ beginCallback("FileChanged");
+ appendArgument("Loc", Loc);
+ appendArgument("Reason", Reason, FileChangeReasonStrings);
+ appendArgument("FileType", FileType, CharacteristicKindStrings);
+ appendArgument("PrevFID", PrevFID);
+}
+
+// Callback invoked whenever a source file is skipped as the result
+// of header guard optimization.
+void
+PPCallbacksTracker::FileSkipped(const clang::FileEntry &SkippedFile,
+ const clang::Token &FilenameTok,
+ clang::SrcMgr::CharacteristicKind FileType) {
+ beginCallback("FileSkipped");
+ appendArgument("ParentFile", &SkippedFile);
+ appendArgument("FilenameTok", FilenameTok);
+ appendArgument("FileType", FileType, CharacteristicKindStrings);
+}
+
+// Callback invoked whenever an inclusion directive results in a
+// file-not-found error.
+bool
+PPCallbacksTracker::FileNotFound(llvm::StringRef FileName,
+ llvm::SmallVectorImpl<char> &RecoveryPath) {
+ beginCallback("FileNotFound");
+ appendFilePathArgument("FileName", FileName);
+ return false;
+}
+
+// Callback invoked whenever an inclusion directive of
+// any kind (#include, #import, etc.) has been processed, regardless
+// of whether the inclusion will actually result in an inclusion.
+void PPCallbacksTracker::InclusionDirective(
+ clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
+ llvm::StringRef FileName, bool IsAngled,
+ clang::CharSourceRange FilenameRange, const clang::FileEntry *File,
+ llvm::StringRef SearchPath, llvm::StringRef RelativePath,
+ const clang::Module *Imported) {
+ beginCallback("InclusionDirective");
+ appendArgument("IncludeTok", IncludeTok);
+ appendFilePathArgument("FileName", FileName);
+ appendArgument("IsAngled", IsAngled);
+ appendArgument("FilenameRange", FilenameRange);
+ appendArgument("File", File);
+ appendFilePathArgument("SearchPath", SearchPath);
+ appendFilePathArgument("RelativePath", RelativePath);
+ appendArgument("Imported", Imported);
+}
+
+// Callback invoked whenever there was an explicit module-import
+// syntax.
+void PPCallbacksTracker::moduleImport(clang::SourceLocation ImportLoc,
+ clang::ModuleIdPath Path,
+ const clang::Module *Imported) {
+ beginCallback("moduleImport");
+ appendArgument("ImportLoc", ImportLoc);
+ appendArgument("Path", Path);
+ appendArgument("Imported", Imported);
+}
+
+// Callback invoked when the end of the main file is reached.
+// No subsequent callbacks will be made.
+void PPCallbacksTracker::EndOfMainFile() { beginCallback("EndOfMainFile"); }
+
+// Callback invoked when a #ident or #sccs directive is read.
+void PPCallbacksTracker::Ident(clang::SourceLocation Loc, llvm::StringRef Str) {
+ beginCallback("Ident");
+ appendArgument("Loc", Loc);
+ appendArgument("Str", Str);
+}
+
+// Callback invoked when start reading any pragma directive.
+void
+PPCallbacksTracker::PragmaDirective(clang::SourceLocation Loc,
+ clang::PragmaIntroducerKind Introducer) {
+ beginCallback("PragmaDirective");
+ appendArgument("Loc", Loc);
+ appendArgument("Introducer", Introducer, PragmaIntroducerKindStrings);
+}
+
+// Callback invoked when a #pragma comment directive is read.
+void PPCallbacksTracker::PragmaComment(clang::SourceLocation Loc,
+ const clang::IdentifierInfo *Kind,
+ llvm::StringRef Str) {
+ beginCallback("PragmaComment");
+ appendArgument("Loc", Loc);
+ appendArgument("Kind", Kind);
+ appendArgument("Str", Str);
+}
+
+// Callback invoked when a #pragma detect_mismatch directive is
+// read.
+void PPCallbacksTracker::PragmaDetectMismatch(clang::SourceLocation Loc,
+ llvm::StringRef Name,
+ llvm::StringRef Value) {
+ beginCallback("PragmaDetectMismatch");
+ appendArgument("Loc", Loc);
+ appendArgument("Name", Name);
+ appendArgument("Value", Value);
+}
+
+// Callback invoked when a #pragma clang __debug directive is read.
+void PPCallbacksTracker::PragmaDebug(clang::SourceLocation Loc,
+ llvm::StringRef DebugType) {
+ beginCallback("PragmaDebug");
+ appendArgument("Loc", Loc);
+ appendArgument("DebugType", DebugType);
+}
+
+// Callback invoked when a #pragma message directive is read.
+void PPCallbacksTracker::PragmaMessage(
+ clang::SourceLocation Loc, llvm::StringRef Namespace,
+ clang::PPCallbacks::PragmaMessageKind Kind, llvm::StringRef Str) {
+ beginCallback("PragmaMessage");
+ appendArgument("Loc", Loc);
+ appendArgument("Namespace", Namespace);
+ appendArgument("Kind", Kind, PragmaMessageKindStrings);
+ appendArgument("Str", Str);
+}
+
+// Callback invoked when a #pragma gcc dianostic push directive
+// is read.
+void PPCallbacksTracker::PragmaDiagnosticPush(clang::SourceLocation Loc,
+ llvm::StringRef Namespace) {
+ beginCallback("PragmaDiagnosticPush");
+ appendArgument("Loc", Loc);
+ appendArgument("Namespace", Namespace);
+}
+
+// Callback invoked when a #pragma gcc dianostic pop directive
+// is read.
+void PPCallbacksTracker::PragmaDiagnosticPop(clang::SourceLocation Loc,
+ llvm::StringRef Namespace) {
+ beginCallback("PragmaDiagnosticPop");
+ appendArgument("Loc", Loc);
+ appendArgument("Namespace", Namespace);
+}
+
+// Callback invoked when a #pragma gcc dianostic directive is read.
+void PPCallbacksTracker::PragmaDiagnostic(clang::SourceLocation Loc,
+ llvm::StringRef Namespace,
+ clang::diag::Severity Mapping,
+ llvm::StringRef Str) {
+ beginCallback("PragmaDiagnostic");
+ appendArgument("Loc", Loc);
+ appendArgument("Namespace", Namespace);
+ appendArgument("Mapping", (unsigned)Mapping, MappingStrings);
+ appendArgument("Str", Str);
+}
+
+// Called when an OpenCL extension is either disabled or
+// enabled with a pragma.
+void PPCallbacksTracker::PragmaOpenCLExtension(
+ clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name,
+ clang::SourceLocation StateLoc, unsigned State) {
+ beginCallback("PragmaOpenCLExtension");
+ appendArgument("NameLoc", NameLoc);
+ appendArgument("Name", Name);
+ appendArgument("StateLoc", StateLoc);
+ appendArgument("State", (int)State);
+}
+
+// Callback invoked when a #pragma warning directive is read.
+void PPCallbacksTracker::PragmaWarning(clang::SourceLocation Loc,
+ llvm::StringRef WarningSpec,
+ llvm::ArrayRef<int> Ids) {
+ beginCallback("PragmaWarning");
+ appendArgument("Loc", Loc);
+ appendArgument("WarningSpec", WarningSpec);
+
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "[";
+ for (int i = 0, e = Ids.size(); i != e; ++i) {
+ if (i)
+ SS << ", ";
+ SS << Ids[i];
+ }
+ SS << "]";
+ appendArgument("Ids", SS.str());
+}
+
+// Callback invoked when a #pragma warning(push) directive is read.
+void PPCallbacksTracker::PragmaWarningPush(clang::SourceLocation Loc,
+ int Level) {
+ beginCallback("PragmaWarningPush");
+ appendArgument("Loc", Loc);
+ appendArgument("Level", Level);
+}
+
+// Callback invoked when a #pragma warning(pop) directive is read.
+void PPCallbacksTracker::PragmaWarningPop(clang::SourceLocation Loc) {
+ beginCallback("PragmaWarningPop");
+ appendArgument("Loc", Loc);
+}
+
+// Called by Preprocessor::HandleMacroExpandedIdentifier when a
+// macro invocation is found.
+void
+PPCallbacksTracker::MacroExpands(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MacroDefinition,
+ clang::SourceRange Range,
+ const clang::MacroArgs *Args) {
+ beginCallback("MacroExpands");
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDefinition", MacroDefinition);
+ appendArgument("Range", Range);
+ appendArgument("Args", Args);
+}
+
+// Hook called whenever a macro definition is seen.
+void
+PPCallbacksTracker::MacroDefined(const clang::Token &MacroNameTok,
+ const clang::MacroDirective *MacroDirective) {
+ beginCallback("MacroDefined");
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDirective", MacroDirective);
+}
+
+// Hook called whenever a macro #undef is seen.
+void PPCallbacksTracker::MacroUndefined(
+ const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MacroDefinition,
+ const clang::MacroDirective *Undef) {
+ beginCallback("MacroUndefined");
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever the 'defined' operator is seen.
+void PPCallbacksTracker::Defined(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MacroDefinition,
+ clang::SourceRange Range) {
+ beginCallback("Defined");
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDefinition", MacroDefinition);
+ appendArgument("Range", Range);
+}
+
+// Hook called when a source range is skipped.
+void PPCallbacksTracker::SourceRangeSkipped(clang::SourceRange Range) {
+ beginCallback("SourceRangeSkipped");
+ appendArgument("Range", Range);
+}
+
+// Hook called whenever an #if is seen.
+void PPCallbacksTracker::If(clang::SourceLocation Loc,
+ clang::SourceRange ConditionRange,
+ ConditionValueKind ConditionValue) {
+ beginCallback("If");
+ appendArgument("Loc", Loc);
+ appendArgument("ConditionRange", ConditionRange);
+ appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);
+}
+
+// Hook called whenever an #elif is seen.
+void PPCallbacksTracker::Elif(clang::SourceLocation Loc,
+ clang::SourceRange ConditionRange,
+ ConditionValueKind ConditionValue,
+ clang::SourceLocation IfLoc) {
+ beginCallback("Elif");
+ appendArgument("Loc", Loc);
+ appendArgument("ConditionRange", ConditionRange);
+ appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);
+ appendArgument("IfLoc", IfLoc);
+}
+
+// Hook called whenever an #ifdef is seen.
+void PPCallbacksTracker::Ifdef(clang::SourceLocation Loc,
+ const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MacroDefinition) {
+ beginCallback("Ifdef");
+ appendArgument("Loc", Loc);
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever an #ifndef is seen.
+void PPCallbacksTracker::Ifndef(clang::SourceLocation Loc,
+ const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MacroDefinition) {
+ beginCallback("Ifndef");
+ appendArgument("Loc", Loc);
+ appendArgument("MacroNameTok", MacroNameTok);
+ appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever an #else is seen.
+void PPCallbacksTracker::Else(clang::SourceLocation Loc,
+ clang::SourceLocation IfLoc) {
+ beginCallback("Else");
+ appendArgument("Loc", Loc);
+ appendArgument("IfLoc", IfLoc);
+}
+
+// Hook called whenever an #endif is seen.
+void PPCallbacksTracker::Endif(clang::SourceLocation Loc,
+ clang::SourceLocation IfLoc) {
+ beginCallback("Endif");
+ appendArgument("Loc", Loc);
+ appendArgument("IfLoc", IfLoc);
+}
+
+// Helper functions.
+
+// Start a new callback.
+void PPCallbacksTracker::beginCallback(const char *Name) {
+ DisableTrace = Ignore.count(std::string(Name));
+ if (DisableTrace)
+ return;
+ CallbackCalls.push_back(CallbackCall(Name));
+}
+
+// Append a bool argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, bool Value) {
+ appendArgument(Name, (Value ? "true" : "false"));
+}
+
+// Append an int argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, int Value) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << Value;
+ appendArgument(Name, SS.str());
+}
+
+// Append a string argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, const char *Value) {
+ if (DisableTrace)
+ return;
+ CallbackCalls.back().Arguments.push_back(Argument(Name, Value));
+}
+
+// Append a string object argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ llvm::StringRef Value) {
+ appendArgument(Name, Value.str());
+}
+
+// Append a string object argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const std::string &Value) {
+ appendArgument(Name, Value.c_str());
+}
+
+// Append a token argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::Token &Value) {
+ appendArgument(Name, PP.getSpelling(Value));
+}
+
+// Append an enum argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, int Value,
+ const char *const Strings[]) {
+ appendArgument(Name, Strings[Value]);
+}
+
+// Append a FileID argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, clang::FileID Value) {
+ if (Value.isInvalid()) {
+ appendArgument(Name, "(invalid)");
+ return;
+ }
+ const clang::FileEntry *FileEntry =
+ PP.getSourceManager().getFileEntryForID(Value);
+ if (!FileEntry) {
+ appendArgument(Name, "(getFileEntryForID failed)");
+ return;
+ }
+ appendFilePathArgument(Name, FileEntry->getName());
+}
+
+// Append a FileEntry argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::FileEntry *Value) {
+ if (!Value) {
+ appendArgument(Name, "(null)");
+ return;
+ }
+ appendFilePathArgument(Name, Value->getName());
+}
+
+// Append a SourceLocation argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ clang::SourceLocation Value) {
+ if (Value.isInvalid()) {
+ appendArgument(Name, "(invalid)");
+ return;
+ }
+ appendArgument(Name, getSourceLocationString(PP, Value).c_str());
+}
+
+// Append a SourceRange argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ clang::SourceRange Value) {
+ if (DisableTrace)
+ return;
+ if (Value.isInvalid()) {
+ appendArgument(Name, "(invalid)");
+ return;
+ }
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "[" << getSourceLocationString(PP, Value.getBegin()) << ", "
+ << getSourceLocationString(PP, Value.getEnd()) << "]";
+ appendArgument(Name, SS.str());
+}
+
+// Append a CharSourceRange argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ clang::CharSourceRange Value) {
+ if (Value.isInvalid()) {
+ appendArgument(Name, "(invalid)");
+ return;
+ }
+ appendArgument(Name, getSourceString(Value).str().c_str());
+}
+
+// Append a SourceLocation argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ clang::ModuleIdPath Value) {
+ if (DisableTrace)
+ return;
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "[";
+ for (int I = 0, E = Value.size(); I != E; ++I) {
+ if (I)
+ SS << ", ";
+ SS << "{"
+ << "Name: " << Value[I].first->getName() << ", "
+ << "Loc: " << getSourceLocationString(PP, Value[I].second) << "}";
+ }
+ SS << "]";
+ appendArgument(Name, SS.str());
+}
+
+// Append an IdentifierInfo argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::IdentifierInfo *Value) {
+ if (!Value) {
+ appendArgument(Name, "(null)");
+ return;
+ }
+ appendArgument(Name, Value->getName().str().c_str());
+}
+
+// Append a MacroDirective argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::MacroDirective *Value) {
+ if (!Value) {
+ appendArgument(Name, "(null)");
+ return;
+ }
+ appendArgument(Name, MacroDirectiveKindStrings[Value->getKind()]);
+}
+
+// Append a MacroDefinition argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::MacroDefinition &Value) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "[";
+ bool Any = false;
+ if (Value.getLocalDirective()) {
+ SS << "(local)";
+ Any = true;
+ }
+ for (auto *MM : Value.getModuleMacros()) {
+ if (Any) SS << ", ";
+ SS << MM->getOwningModule()->getFullModuleName();
+ }
+ SS << "]";
+ appendArgument(Name, SS.str());
+}
+
+// Append a MacroArgs argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::MacroArgs *Value) {
+ if (!Value) {
+ appendArgument(Name, "(null)");
+ return;
+ }
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "[";
+
+ // Each argument is is a series of contiguous Tokens, terminated by a eof.
+ // Go through each argument printing tokens until we reach eof.
+ for (unsigned I = 0; I < Value->getNumMacroArguments(); ++I) {
+ const clang::Token *Current = Value->getUnexpArgument(I);
+ if (I)
+ SS << ", ";
+ bool First = true;
+ while (Current->isNot(clang::tok::eof)) {
+ if (!First)
+ SS << " ";
+ // We need to be careful here because the arguments might not be legal in
+ // YAML, so we use the token name for anything but identifiers and
+ // numeric literals.
+ if (Current->isAnyIdentifier() ||
+ Current->is(clang::tok::numeric_constant)) {
+ SS << PP.getSpelling(*Current);
+ } else {
+ SS << "<" << Current->getName() << ">";
+ }
+ ++Current;
+ First = false;
+ }
+ }
+ SS << "]";
+ appendArgument(Name, SS.str());
+}
+
+// Append a Module argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+ const clang::Module *Value) {
+ if (!Value) {
+ appendArgument(Name, "(null)");
+ return;
+ }
+ appendArgument(Name, Value->Name.c_str());
+}
+
+// Append a double-quoted argument to the top trace item.
+void PPCallbacksTracker::appendQuotedArgument(const char *Name,
+ const std::string &Value) {
+ std::string Str;
+ llvm::raw_string_ostream SS(Str);
+ SS << "\"" << Value << "\"";
+ appendArgument(Name, SS.str());
+}
+
+// Append a double-quoted file path argument to the top trace item.
+void PPCallbacksTracker::appendFilePathArgument(const char *Name,
+ llvm::StringRef Value) {
+ std::string Path(Value);
+ // YAML treats backslash as escape, so use forward slashes.
+ std::replace(Path.begin(), Path.end(), '\\', '/');
+ appendQuotedArgument(Name, Path);
+}
+
+// Get the raw source string of the range.
+llvm::StringRef
+PPCallbacksTracker::getSourceString(clang::CharSourceRange Range) {
+ const char *B = PP.getSourceManager().getCharacterData(Range.getBegin());
+ const char *E = PP.getSourceManager().getCharacterData(Range.getEnd());
+ return llvm::StringRef(B, E - B);
+}
--- /dev/null
+//===--- PPCallbacksTracker.h - Preprocessor tracking -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Classes and definitions for preprocessor tracking.
+///
+/// The core definition is the PPCallbacksTracker class, derived from Clang's
+/// PPCallbacks class from the Lex library, which overrides all the callbacks
+/// and collects information about each callback call, saving it in a
+/// data structure built up of CallbackCall and Argument objects, which
+/// record the preprocessor callback name and arguments in high-level string
+/// form for later inspection.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef PPTRACE_PPCALLBACKSTRACKER_H
+#define PPTRACE_PPCALLBACKSTRACKER_H
+
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+/// \brief This class represents one callback function argument by name
+/// and value.
+class Argument {
+public:
+ Argument(llvm::StringRef Name, llvm::StringRef Value)
+ : Name(Name), Value(Value) {}
+ Argument() = default;
+
+ std::string Name;
+ std::string Value;
+};
+
+/// \brief This class represents one callback call by name and an array
+/// of arguments.
+class CallbackCall {
+public:
+ CallbackCall(llvm::StringRef Name) : Name(Name) {}
+ CallbackCall() = default;
+
+ std::string Name;
+ std::vector<Argument> Arguments;
+};
+
+/// \brief This class overrides the PPCallbacks class for tracking preprocessor
+/// activity by means of its callback functions.
+///
+/// This object is given a vector for storing the trace information, built up
+/// of CallbackCall and subordinate Argument objects for representing the
+/// callback calls and their arguments. It's a reference so the vector can
+/// exist beyond the lifetime of this object, because it's deleted by the
+/// preprocessor automatically in its destructor.
+///
+/// This class supports a mechanism for inhibiting trace output for
+/// specific callbacks by name, for the purpose of eliminating output for
+/// callbacks of no interest that might clutter the output.
+///
+/// Following the constructor and destructor function declarations, the
+/// overidden callback functions are defined. The remaining functions are
+/// helpers for recording the trace data, to reduce the coupling between it
+/// and the recorded data structure.
+class PPCallbacksTracker : public clang::PPCallbacks {
+public:
+ /// \brief Note that all of the arguments are references, and owned
+ /// by the caller.
+ /// \param Ignore - Set of names of callbacks to ignore.
+ /// \param CallbackCalls - Trace buffer.
+ /// \param PP - The preprocessor. Needed for getting some argument strings.
+ PPCallbacksTracker(llvm::SmallSet<std::string, 4> &Ignore,
+ std::vector<CallbackCall> &CallbackCalls,
+ clang::Preprocessor &PP);
+
+ ~PPCallbacksTracker() override;
+
+ // Overidden callback functions.
+
+ void FileChanged(clang::SourceLocation Loc,
+ clang::PPCallbacks::FileChangeReason Reason,
+ clang::SrcMgr::CharacteristicKind FileType,
+ clang::FileID PrevFID = clang::FileID()) override;
+ void FileSkipped(const clang::FileEntry &SkippedFile,
+ const clang::Token &FilenameTok,
+ clang::SrcMgr::CharacteristicKind FileType) override;
+ bool FileNotFound(llvm::StringRef FileName,
+ llvm::SmallVectorImpl<char> &RecoveryPath) override;
+ void InclusionDirective(clang::SourceLocation HashLoc,
+ const clang::Token &IncludeTok,
+ llvm::StringRef FileName, bool IsAngled,
+ clang::CharSourceRange FilenameRange,
+ const clang::FileEntry *File,
+ llvm::StringRef SearchPath,
+ llvm::StringRef RelativePath,
+ const clang::Module *Imported) override;
+ void moduleImport(clang::SourceLocation ImportLoc, clang::ModuleIdPath Path,
+ const clang::Module *Imported) override;
+ void EndOfMainFile() override;
+ void Ident(clang::SourceLocation Loc, llvm::StringRef str) override;
+ void PragmaDirective(clang::SourceLocation Loc,
+ clang::PragmaIntroducerKind Introducer) override;
+ void PragmaComment(clang::SourceLocation Loc,
+ const clang::IdentifierInfo *Kind,
+ llvm::StringRef Str) override;
+ void PragmaDetectMismatch(clang::SourceLocation Loc, llvm::StringRef Name,
+ llvm::StringRef Value) override;
+ void PragmaDebug(clang::SourceLocation Loc,
+ llvm::StringRef DebugType) override;
+ void PragmaMessage(clang::SourceLocation Loc, llvm::StringRef Namespace,
+ clang::PPCallbacks::PragmaMessageKind Kind,
+ llvm::StringRef Str) override;
+ void PragmaDiagnosticPush(clang::SourceLocation Loc,
+ llvm::StringRef Namespace) override;
+ void PragmaDiagnosticPop(clang::SourceLocation Loc,
+ llvm::StringRef Namespace) override;
+ void PragmaDiagnostic(clang::SourceLocation Loc, llvm::StringRef Namespace,
+ clang::diag::Severity mapping,
+ llvm::StringRef Str) override;
+ void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
+ const clang::IdentifierInfo *Name,
+ clang::SourceLocation StateLoc,
+ unsigned State) override;
+ void PragmaWarning(clang::SourceLocation Loc, llvm::StringRef WarningSpec,
+ llvm::ArrayRef<int> Ids) override;
+ void PragmaWarningPush(clang::SourceLocation Loc, int Level) override;
+ void PragmaWarningPop(clang::SourceLocation Loc) override;
+ void MacroExpands(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD, clang::SourceRange Range,
+ const clang::MacroArgs *Args) override;
+ void MacroDefined(const clang::Token &MacroNameTok,
+ const clang::MacroDirective *MD) override;
+ void MacroUndefined(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD,
+ const clang::MacroDirective *Undef) override;
+ void Defined(const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD,
+ clang::SourceRange Range) override;
+ void SourceRangeSkipped(clang::SourceRange Range) override;
+ void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+ ConditionValueKind ConditionValue) override;
+ void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+ ConditionValueKind ConditionValue, clang::SourceLocation IfLoc) override;
+ void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) override;
+ void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+ const clang::MacroDefinition &MD) override;
+ void Else(clang::SourceLocation Loc,
+ clang::SourceLocation IfLoc) override;
+ void Endif(clang::SourceLocation Loc,
+ clang::SourceLocation IfLoc) override;
+
+ // Helper functions.
+
+ /// \brief Start a new callback.
+ void beginCallback(const char *Name);
+
+ /// \brief Append a string to the top trace item.
+ void append(const char *Str);
+
+ /// \brief Append a bool argument to the top trace item.
+ void appendArgument(const char *Name, bool Value);
+
+ /// \brief Append an int argument to the top trace item.
+ void appendArgument(const char *Name, int Value);
+
+ /// \brief Append a string argument to the top trace item.
+ void appendArgument(const char *Name, const char *Value);
+
+ /// \brief Append a string reference object argument to the top trace item.
+ void appendArgument(const char *Name, llvm::StringRef Value);
+
+ /// \brief Append a string object argument to the top trace item.
+ void appendArgument(const char *Name, const std::string &Value);
+
+ /// \brief Append a token argument to the top trace item.
+ void appendArgument(const char *Name, const clang::Token &Value);
+
+ /// \brief Append an enum argument to the top trace item.
+ void appendArgument(const char *Name, int Value, const char *const Strings[]);
+
+ /// \brief Append a FileID argument to the top trace item.
+ void appendArgument(const char *Name, clang::FileID Value);
+
+ /// \brief Append a FileEntry argument to the top trace item.
+ void appendArgument(const char *Name, const clang::FileEntry *Value);
+
+ /// \brief Append a SourceLocation argument to the top trace item.
+ void appendArgument(const char *Name, clang::SourceLocation Value);
+
+ /// \brief Append a SourceRange argument to the top trace item.
+ void appendArgument(const char *Name, clang::SourceRange Value);
+
+ /// \brief Append a CharSourceRange argument to the top trace item.
+ void appendArgument(const char *Name, clang::CharSourceRange Value);
+
+ /// \brief Append a ModuleIdPath argument to the top trace item.
+ void appendArgument(const char *Name, clang::ModuleIdPath Value);
+
+ /// \brief Append an IdentifierInfo argument to the top trace item.
+ void appendArgument(const char *Name, const clang::IdentifierInfo *Value);
+
+ /// \brief Append a MacroDirective argument to the top trace item.
+ void appendArgument(const char *Name, const clang::MacroDirective *Value);
+
+ /// \brief Append a MacroDefinition argument to the top trace item.
+ void appendArgument(const char *Name, const clang::MacroDefinition &Value);
+
+ /// \brief Append a MacroArgs argument to the top trace item.
+ void appendArgument(const char *Name, const clang::MacroArgs *Value);
+
+ /// \brief Append a Module argument to the top trace item.
+ void appendArgument(const char *Name, const clang::Module *Value);
+
+ /// \brief Append a double-quoted argument to the top trace item.
+ void appendQuotedArgument(const char *Name, const std::string &Value);
+
+ /// \brief Append a double-quoted file path argument to the top trace item.
+ void appendFilePathArgument(const char *Name, llvm::StringRef Value);
+
+ /// \brief Get the raw source string of the range.
+ llvm::StringRef getSourceString(clang::CharSourceRange Range);
+
+ /// \brief Callback trace information.
+ /// We use a reference so the trace will be preserved for the caller
+ /// after this object is destructed.
+ std::vector<CallbackCall> &CallbackCalls;
+
+ /// \brief Names of callbacks to ignore.
+ llvm::SmallSet<std::string, 4> &Ignore;
+
+ /// \brief Inhibit trace while this is set.
+ bool DisableTrace;
+
+ clang::Preprocessor &PP;
+};
+
+#endif // PPTRACE_PPCALLBACKSTRACKER_H
--- /dev/null
+//===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements pp-trace, a tool for displaying a textual trace
+// of the Clang preprocessor activity. It's based on a derivation of the
+// PPCallbacks class, that once registerd with Clang, receives callback calls
+// to its virtual members, and outputs the information passed to the callbacks
+// in a high-level YAML format.
+//
+// The pp-trace tool also serves as the basis for a test of the PPCallbacks
+// mechanism.
+//
+// The pp-trace tool supports the following general command line format:
+//
+// pp-trace [pp-trace options] (source file) [compiler options]
+//
+// Basically you put the pp-trace options first, then the source file or files,
+// and then any options you want to pass to the compiler.
+//
+// These are the pp-trace options:
+//
+// -ignore (callback list) Don't display output for a comma-separated
+// list of callbacks, i.e.:
+// -ignore "FileChanged,InclusionDirective"
+//
+// -output (file) Output trace to the given file in a YAML
+// format, e.g.:
+//
+// ---
+// - Callback: Name
+// Argument1: Value1
+// Argument2: Value2
+// (etc.)
+// ...
+//
+// Future Directions:
+//
+// 1. Add option opposite to "-ignore" that specifys a comma-separated option
+// list of callbacs. Perhaps "-only" or "-exclusive".
+//
+//===----------------------------------------------------------------------===//
+
+#include "PPCallbacksTracker.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+using namespace llvm;
+using namespace llvm::opt;
+
+// Options:
+
+// Collect the source files.
+static cl::list<std::string> SourcePaths(cl::Positional,
+ cl::desc("<source0> [... <sourceN>]"),
+ cl::OneOrMore);
+
+// Option to specify a list or one or more callback names to ignore.
+static cl::opt<std::string> IgnoreCallbacks(
+ "ignore", cl::init(""),
+ cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\"."));
+
+// Option to specify the trace output file name.
+static cl::opt<std::string> OutputFileName(
+ "output", cl::init(""),
+ cl::desc("Output trace to the given file name or '-' for stdout."));
+
+// Collect all other arguments, which will be passed to the front end.
+static cl::list<std::string>
+ CC1Arguments(cl::ConsumeAfter,
+ cl::desc("<arguments to be passed to front end>..."));
+
+// Frontend action stuff:
+
+namespace {
+// Consumer is responsible for setting up the callbacks.
+class PPTraceConsumer : public ASTConsumer {
+public:
+ PPTraceConsumer(SmallSet<std::string, 4> &Ignore,
+ std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) {
+ // PP takes ownership.
+ PP.addPPCallbacks(llvm::make_unique<PPCallbacksTracker>(Ignore,
+ CallbackCalls, PP));
+ }
+};
+
+class PPTraceAction : public SyntaxOnlyAction {
+public:
+ PPTraceAction(SmallSet<std::string, 4> &Ignore,
+ std::vector<CallbackCall> &CallbackCalls)
+ : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
+
+protected:
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+ return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls,
+ CI.getPreprocessor());
+ }
+
+private:
+ SmallSet<std::string, 4> &Ignore;
+ std::vector<CallbackCall> &CallbackCalls;
+};
+
+class PPTraceFrontendActionFactory : public FrontendActionFactory {
+public:
+ PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore,
+ std::vector<CallbackCall> &CallbackCalls)
+ : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
+
+ PPTraceAction *create() override {
+ return new PPTraceAction(Ignore, CallbackCalls);
+ }
+
+private:
+ SmallSet<std::string, 4> &Ignore;
+ std::vector<CallbackCall> &CallbackCalls;
+};
+} // namespace
+
+// Output the trace given its data structure and a stream.
+static int outputPPTrace(std::vector<CallbackCall> &CallbackCalls,
+ llvm::raw_ostream &OS) {
+ // Mark start of document.
+ OS << "---\n";
+
+ for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(),
+ E = CallbackCalls.end();
+ I != E; ++I) {
+ const CallbackCall &Callback = *I;
+ OS << "- Callback: " << Callback.Name << "\n";
+
+ for (auto AI = Callback.Arguments.begin(), AE = Callback.Arguments.end();
+ AI != AE; ++AI) {
+ const Argument &Arg = *AI;
+ OS << " " << Arg.Name << ": " << Arg.Value << "\n";
+ }
+ }
+
+ // Mark end of document.
+ OS << "...\n";
+
+ return 0;
+}
+
+// Program entry point.
+int main(int Argc, const char **Argv) {
+
+ // Parse command line.
+ cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n");
+
+ // Parse the IgnoreCallbacks list into strings.
+ SmallVector<StringRef, 32> IgnoreCallbacksStrings;
+ StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",",
+ /*MaxSplit=*/ -1, /*KeepEmpty=*/false);
+ SmallSet<std::string, 4> Ignore;
+ for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(),
+ E = IgnoreCallbacksStrings.end();
+ I != E; ++I)
+ Ignore.insert(*I);
+
+ // Create the compilation database.
+ SmallString<256> PathBuf;
+ sys::fs::current_path(PathBuf);
+ std::unique_ptr<CompilationDatabase> Compilations;
+ Compilations.reset(
+ new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
+
+ // Store the callback trace information here.
+ std::vector<CallbackCall> CallbackCalls;
+
+ // Create the tool and run the compilation.
+ ClangTool Tool(*Compilations, SourcePaths);
+ PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls);
+ int HadErrors = Tool.run(&Factory);
+
+ // If we had errors, exit early.
+ if (HadErrors)
+ return HadErrors;
+
+ // Do the output.
+ if (!OutputFileName.size()) {
+ HadErrors = outputPPTrace(CallbackCalls, llvm::outs());
+ } else {
+ // Set up output file.
+ std::error_code EC;
+ llvm::tool_output_file Out(OutputFileName, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << "pp-trace: error creating " << OutputFileName << ":"
+ << EC.message() << "\n";
+ return 1;
+ }
+
+ HadErrors = outputPPTrace(CallbackCalls, Out.os());
+
+ // Tell tool_output_file that we want to keep the file.
+ if (HadErrors == 0)
+ Out.keep();
+ }
+
+ return HadErrors;
+}
--- /dev/null
+BasedOnStyle: LLVM
+ColumnLimit: 0
--- /dev/null
+# Test runner infrastructure for Clang-based tools. This configures the Clang
+# test trees for use by Lit, and delegates to LLVM's lit test handlers.
+#
+# Note that currently we don't support stand-alone builds of Clang, you must
+# be building Clang from within a combined LLVM+Clang checkout..
+
+set(CLANG_TOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
+set(CLANG_TOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
+
+if (CMAKE_CFG_INTDIR STREQUAL ".")
+ set(LLVM_BUILD_MODE ".")
+else ()
+ set(LLVM_BUILD_MODE "%(build_mode)s")
+endif ()
+
+string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} CLANG_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+ )
+
+option(CLANG_TOOLS_TEST_USE_VG "Run Clang tools' tests under Valgrind" OFF)
+if(CLANG_TOOLS_TEST_USE_VG)
+ set(CLANG_TOOLS_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
+endif()
+
+set(CLANG_TOOLS_TEST_DEPS
+ # clang-tidy tests require it.
+ clang-headers
+
+ # For the clang-tidy libclang integration test.
+ c-index-test
+ # For the clang-apply-replacements test that uses clang-rename.
+ clang-rename
+
+ # Individual tools we test.
+ clang-apply-replacements
+ clang-change-namespace
+ clangd
+ clang-include-fixer
+ clang-move
+ clang-query
+ clang-reorder-fields
+ clang-tidy
+ find-all-symbols
+ modularize
+ pp-trace
+
+ # Unit tests
+ ExtraToolsUnitTests
+ )
+
+set(llvm_utils
+ FileCheck count not
+ )
+
+foreach(t ${llvm_utils})
+ if(TARGET ${t})
+ list(APPEND CLANG_TOOLS_TEST_DEPS ${t})
+ endif()
+endforeach()
+
+add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CLANG_TOOLS_TEST_DEPS}
+ ARGS ${CLANG_TOOLS_TEST_EXTRA_ARGS}
+ )
+
+set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests")
--- /dev/null
+# -*- Python -*-
+
+import platform
+
+import lit.formats
+
+config.name = "Extra Tools Unit Tests"
+config.suffixes = [] # Seems not to matter for google tests?
+
+# Test Source and Exec root dirs both point to the same directory where google
+# test binaries are built.
+extra_tools_obj_dir = getattr(config, 'extra_tools_obj_dir', None)
+if extra_tools_obj_dir is not None:
+ config.test_source_root = extra_tools_obj_dir
+ config.test_exec_root = config.test_source_root
+
+# All GoogleTests are named to have 'Tests' as their suffix. The '.' option is
+# a special value for GoogleTest indicating that it should look through the
+# entire testsuite recursively for tests (alternatively, one could provide a
+# ;-separated list of subdirectories).
+config.test_format = lit.formats.GoogleTest('.', 'Tests')
+
+# If the site-specific configuration wasn't loaded (e.g. the build system failed
+# to create it or the user is running a test file directly) try to come up with
+# sane config options.
+if config.test_exec_root is None:
+ # Look for a --param=extra_tools_unit_site_config option.
+ site_cfg = lit_config.params.get('extra_tools_unit_site_config', None)
+ if site_cfg and os.path.exists(site_cfg):
+ lit_config.load_config(config, site_cfg)
+ raise SystemExit
+
+ # FIXME: Support out-of-tree builds? See clang/test/Unit/lit.cfg if we care.
+
+shlibpath_var = ''
+if platform.system() == 'Linux':
+ shlibpath_var = 'LD_LIBRARY_PATH'
+elif platform.system() == 'Darwin':
+ shlibpath_var = 'DYLD_LIBRARY_PATH'
+elif platform.system() == 'Windows':
+ shlibpath_var = 'PATH'
+
+# Point the dynamic loader at dynamic libraries in 'lib'.
+llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
+if not llvm_libs_dir:
+ lit_config.fatal('No LLVM libs dir set!')
+shlibpath = os.path.pathsep.join((llvm_libs_dir,
+ config.environment.get(shlibpath_var,'')))
+
+# Win32 seeks DLLs along %PATH%.
+if sys.platform in ['win32', 'cygwin'] and os.path.isdir(config.shlibdir):
+ shlibpath = os.path.pathsep.join((config.shlibdir, shlibpath))
+
+config.environment[shlibpath_var] = shlibpath
--- /dev/null
+@LIT_SITE_CFG_IN_HEADER@
+
+config.extra_tools_obj_dir = "@CLANG_TOOLS_BINARY_DIR@/unittests"
+config.extra_tools_src_dir = "@CLANG_TOOLS_SOURCE_DIR@/unittests"
+config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.shlibdir = "@SHLIBDIR@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+lit_config.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/Unit/lit.cfg")
--- /dev/null
+namespace std {
+ class STD {};
+}
+
+using namespace std;
--- /dev/null
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- -std=c++11 | sed 's,// CHECK.*,,' | FileCheck %s
+
+template <class T>
+class function;
+template <class R, class... ArgTypes>
+class function<R(ArgTypes...)> {
+public:
+ template <typename Functor>
+ function(Functor f) {}
+ R operator()(ArgTypes...) const {}
+};
+
+namespace x {
+// CHECK: namespace x {
+class X {};
+}
+
+namespace na {
+namespace nb {
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+void f(function<void(int)> func, int param) { func(param); }
+void g() { f([](int x) {}, 1); }
+
+// x::X in function type parameter list will have translation unit context, so
+// we simply replace it with fully-qualified name.
+using TX = function<x::X(x::X)>;
+// CHECK: using TX = function<X(x::X)>;
+
+class A {};
+using TA = function<A(A)>;
+// CHECK: using TA = function<A(A)>;
+
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
--- /dev/null
+// RUN: cp %S/macro.cpp %T/macro.cpp
+// RUN: echo "#define USING using na::nc::X" > %T/macro.h
+//
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern "macro.cpp" --i %T/macro.cpp --
+// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s
+// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-HEADER %s
+//
+// RUN: cp %S/macro.cpp %T/macro.cpp
+// RUN: echo "#define USING using na::nc::X" > %T/macro.h
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --i %T/macro.cpp --
+// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s
+// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-CHANGED-HEADER %s
+#include "macro.h"
+namespace na { namespace nc { class X{}; } }
+
+namespace na {
+namespace nb {
+USING;
+}
+}
+// CHECK-CC: namespace x {
+// CHECK-CC: namespace y {
+// CHECK-CC: USING;
+// CHECK-CC: } // namespace y
+// CHECK-CC: } // namespace x
+
+// CHECK-HEADER: #define USING using na::nc::X
+
+// CHECK-CHANGED-HEADER: #define USING using ::na::nc::X
--- /dev/null
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- | sed 's,// CHECK.*,,' | FileCheck %s
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+namespace na {
+namespace nb {
+class A {};
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
--- /dev/null
+// RUN: echo "^std::.*$" > %T/white-list.txt
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --whitelist_file %T/white-list.txt %s -- | sed 's,// CHECK.*,,' | FileCheck %s
+
+#include "Inputs/fake-std.h"
+
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+namespace na {
+namespace nb {
+void f() {
+ std::STD x1;
+ STD x2;
+// CHECK: {{^}} std::STD x1;{{$}}
+// CHECK-NEXT: {{^}} STD x2;{{$}}
+}
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
--- /dev/null
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/fixes
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=254 -new-name=Bar -export-fixes=%t/fixes/clang-rename.yaml %t.cpp --
+// RUN: clang-apply-replacements %t
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {}; // CHECK: class Bar {};
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
+// this file.
--- /dev/null
+#ifndef BASIC_H
+#define BASIC_H
+
+
+class Parent {
+public:
+ virtual void func() {}
+};
+
+class Derived : public Parent {
+public:
+ virtual void func() {}
+ // CHECK: virtual void func() override {}
+};
+
+extern void ext(int (&)[5], const Parent &);
+
+void func(int t) {
+ int ints[5];
+ for (unsigned i = 0; i < 5; ++i) {
+ int &e = ints[i];
+ e = t;
+ // CHECK: for (auto & elem : ints) {
+ // CHECK-NEXT: elem = t;
+ }
+
+ Derived d;
+
+ ext(ints, d);
+}
+
+#endif // BASIC_H
--- /dev/null
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+ - DiagnosticName: test-basic
+ Message: Fix
+ FilePath: $(path)/basic.h
+ FileOffset: 242
+ Replacements:
+ - FilePath: $(path)/basic.h
+ Offset: 242
+ Length: 26
+ ReplacementText: 'auto & elem : ints'
+ - FilePath: $(path)/basic.h
+ Offset: 276
+ Length: 22
+ ReplacementText: ''
+ - FilePath: $(path)/basic.h
+ Offset: 298
+ Length: 1
+ ReplacementText: elem
+ - FilePath: $(path)/../basic/basic.h
+ Offset: 148
+ Length: 0
+ ReplacementText: 'override '
+...
--- /dev/null
+---
+MainSourceFile: source2.cpp
+Diagnostics:
+ - DiagnosticName: test-basic
+ Message: Fix
+ FilePath: $(path)/basic.h
+ FileOffset: 148
+ Replacements:
+ - FilePath: $(path)/basic.h
+ Offset: 148
+ Length: 0
+ ReplacementText: 'override '
+...
--- /dev/null
+#ifndef COMMON_H
+#define COMMON_H
+
+extern void ext(int (&)[5]);
+
+void func(int t) {
+ int ints[5];
+ for (unsigned i = 0; i < 5; ++i) {
+ ints[i] = t;
+ }
+
+ int *i = 0;
+
+ ext(ints);
+}
+
+#endif // COMMON_H
--- /dev/null
+There are conflicting changes to $(path)/common.h:
+The following changes conflict:
+ Replace 8:8-8:33 with "auto & i : ints"
+ Replace 8:8-8:33 with "int & elem : ints"
+The following changes conflict:
+ Replace 9:5-9:11 with "elem"
+ Replace 9:5-9:11 with "i"
+The following changes conflict:
+ Remove 12:3-12:14
+ Insert at 12:12 (int*)
+ Replace 12:12-12:12 with "nullptr"
--- /dev/null
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+ - DiagnosticName: test-conflict
+ Message: Fix
+ FilePath: $(path)/common.h
+ FileOffset: 106
+ Replacements:
+ - FilePath: $(path)/common.h
+ Offset: 106
+ Length: 26
+ ReplacementText: 'auto & i : ints'
+ - FilePath: $(path)/common.h
+ Offset: 140
+ Length: 7
+ ReplacementText: i
+ - FilePath: $(path)/common.h
+ Offset: 160
+ Length: 12
+ ReplacementText: ''
+...
--- /dev/null
+---
+MainSourceFile: source2.cpp
+Diagnostics:
+ - DiagnosticName: test-conflict
+ Message: Fix
+ FilePath: $(path)/common.h
+ FileOffset: 106
+ Replacements:
+ - FilePath: $(path)/common.h
+ Offset: 106
+ Length: 26
+ ReplacementText: 'int & elem : ints'
+ - FilePath: $(path)/common.h
+ Offset: 140
+ Length: 7
+ ReplacementText: elem
+ - FilePath: $(path)/common.h
+ Offset: 169
+ Length: 1
+ ReplacementText: nullptr
+...
--- /dev/null
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+ - DiagnosticName: test-conflict
+ Message: Fix
+ FilePath: $(path)/common.h
+ FileOffset: 169
+ Replacements:
+ - FilePath: $(path)/common.h
+ Offset: 169
+ Length: 0
+ ReplacementText: "(int*)"
+...
--- /dev/null
+\r
+// This file intentionally uses a CRLF newlines!\r
+\r
+void foo() {\r
+ int *x = 0;\r
+}\r
--- /dev/null
+\r
+// This file intentionally uses a CRLF newlines!\r
+\r
+void foo() {\r
+ int *x = nullptr;\r
+}\r
--- /dev/null
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+ - DiagnosticName: test-crlf
+ Message: Fix
+ FilePath: $(path)/crlf.cpp
+ FileOffset: 79
+ Replacements:
+ - FilePath: $(path)/crlf.cpp
+ Offset: 79
+ Length: 1
+ ReplacementText: nullptr
+...
--- /dev/null
+class C {};
+
+void f() { // This comment necessary to prevent formatting as void f() { ... }
+ C *a = new C();
+ // CHECK: {{^\ \ auto\ a\ \=\ new\ C\(\);}}
+}
--- /dev/null
+---
+MainSourceFile: no.cpp
+Diagnostics:
+ - DiagnosticName: test-no
+ Message: Fix
+ FilePath: $(path)/no.cpp
+ FileOffset: 94
+ Replacements:
+ - FilePath: $(path)/no.cpp
+ Offset: 94
+ Length: 3
+ ReplacementText: 'auto '
+...
--- /dev/null
+class MyType012345678901234567890123456789 {};
+
+void g(int, int*, int, int*, int, int*, int);
+
+void f() {
+ MyType012345678901234567890123456789 *a =
+ new MyType012345678901234567890123456789();
+ // CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
+
+ int iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;
+ int jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj;
+ int kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk;
+ int mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm;
+ g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, 0, jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj,
+ 0, kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, 0, mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+ // CHECK: g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, nullptr,
+ // CHECK-NEXT: jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj, nullptr,
+ // CHECK-NEXT: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, nullptr,
+ // CHECK-NEXT: mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+
+ delete a;
+}
--- /dev/null
+# These replacements are out of order on purpose to test that they get sorted
+# so that formatting happens correctly.
+---
+MainSourceFile: yes.cpp
+Diagnostics:
+ - DiagnosticName: test-yes
+ Message: Fix
+ FilePath: $(path)/yes.cpp
+ FileOffset: 494
+ Replacements:
+ - FilePath: $(path)/yes.cpp
+ Offset: 494
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 410
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 454
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 108
+ Length: 38
+ ReplacementText: 'auto '
+...
--- /dev/null
+// RUN: mkdir -p %T/Inputs/basic
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/basic/basic.h > %T/Inputs/basic/basic.h
+// RUN: sed "s#\$(path)#%/T/Inputs/basic#" %S/Inputs/basic/file1.yaml > %T/Inputs/basic/file1.yaml
+// RUN: sed "s#\$(path)#%/T/Inputs/basic#" %S/Inputs/basic/file2.yaml > %T/Inputs/basic/file2.yaml
+// RUN: clang-apply-replacements %T/Inputs/basic
+// RUN: FileCheck -input-file=%T/Inputs/basic/basic.h %S/Inputs/basic/basic.h
+//
+// Check that the yaml files are *not* deleted after running clang-apply-replacements without remove-change-desc-files.
+// RUN: ls -1 %T/Inputs/basic | FileCheck %s --check-prefix=YAML
+//
+// Check that the yaml files *are* deleted after running clang-apply-replacements with remove-change-desc-files.
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/basic/basic.h > %T/Inputs/basic/basic.h
+// RUN: clang-apply-replacements -remove-change-desc-files %T/Inputs/basic
+// RUN: ls -1 %T/Inputs/basic | FileCheck %s --check-prefix=NO_YAML
+//
+// YAML: {{^file.\.yaml$}}
+// NO_YAML-NOT: {{^file.\.yaml$}}
--- /dev/null
+// RUN: mkdir -p %T/Inputs/conflict
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file1.yaml > %T/Inputs/conflict/file1.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file2.yaml > %T/Inputs/conflict/file2.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file3.yaml > %T/Inputs/conflict/file3.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/expected.txt > %T/Inputs/conflict/expected.txt
+// RUN: not clang-apply-replacements %T/Inputs/conflict > %T/Inputs/conflict/output.txt 2>&1
+// RUN: diff -b %T/Inputs/conflict/output.txt %T/Inputs/conflict/expected.txt
+//
+// Check that the yaml files are *not* deleted after running clang-apply-replacements without remove-change-desc-files even when there is a failure.
+// RUN: ls -1 %T/Inputs/conflict | FileCheck %s --check-prefix=YAML
+//
+// Check that the yaml files *are* deleted after running clang-apply-replacements with remove-change-desc-files even when there is a failure.
+// RUN: not clang-apply-replacements %T/Inputs/conflict -remove-change-desc-files > %T/Inputs/conflict/output.txt 2>&1
+// RUN: ls -1 %T/Inputs/conflict | FileCheck %s --check-prefix=NO_YAML
+//
+// YAML: {{^file.\.yaml$}}
+// NO_YAML-NOT: {{^file.\.yaml$}}
--- /dev/null
+// RUN: mkdir -p %T/Inputs/crlf
+// RUN: cp %S/Inputs/crlf/crlf.cpp %T/Inputs/crlf/crlf.cpp
+// RUN: sed "s#\$(path)#%/T/Inputs/crlf#" %S/Inputs/crlf/file1.yaml > %T/Inputs/crlf/file1.yaml
+// RUN: clang-apply-replacements %T/Inputs/crlf
+// RUN: diff %T/Inputs/crlf/crlf.cpp %S/Inputs/crlf/crlf.cpp.expected
--- /dev/null
+// RUN: mkdir -p %T/Inputs/format
+//
+// yes.cpp requires formatting after replacements are applied. no.cpp does not.
+// The presence of no.cpp ensures that files that don't need formatting still
+// have their new state written to disk after applying replacements.
+//
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/yes.cpp > %T/Inputs/format/yes.cpp
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/no.cpp > %T/Inputs/format/no.cpp
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/yes.yaml > %T/Inputs/format/yes.yaml
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/no.yaml > %T/Inputs/format/no.yaml
+// RUN: clang-apply-replacements -format %T/Inputs/format
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/yes.cpp %S/Inputs/format/yes.cpp
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/no.cpp %S/Inputs/format/no.cpp
+//
+// RUN not clang-apply-replacements -format=blah %T/Inputs/format
--- /dev/null
+[
+{
+ "directory": "$test_dir/build",
+ "command": "clang++ -o test.o -I../include $test_dir/src/test.cpp",
+ "file": "$test_dir/src/test.cpp"
+}
+]
--- /dev/null
+namespace a {
+enum E1 { Green, Red };
+
+enum class E2 { Yellow };
+
+class C {
+ enum E3 { Blue };
+};
+} // namespace a
--- /dev/null
+#include "function_test.h"
+
+void f() {}
+
+void A::f() {}
--- /dev/null
+void f();
+
+inline int g() { return 0; }
+
+template<typename T>
+void h(T t) {}
+
+template<>
+void h(int t) {}
+
+class A {
+ public:
+ void f();
+};
--- /dev/null
+#include "helper_decls_test.h"
+
+namespace {
+class HelperC1 {
+public:
+ static int I;
+};
+
+int HelperC1::I = 0;
+
+class HelperC2 {};
+
+class HelperC3 {
+ public:
+ static int I;
+};
+
+int HelperC3::I = 0;
+
+void HelperFun1() {}
+
+void HelperFun2() { HelperFun1(); }
+
+const int K1 = 1;
+} // namespace
+
+static const int K2 = 2;
+static void HelperFun3() { K2; }
+
+namespace a {
+
+static const int K3 = 3;
+static const int K4 = HelperC3::I;
+static const int K5 = 5;
+static const int K6 = 6;
+
+static void HelperFun4() {}
+static void HelperFun6() {}
+
+void Class1::f() { HelperFun2(); }
+
+void Class2::f() {
+ HelperFun1();
+ HelperFun3();
+}
+
+void Class3::f() { HelperC1::I; }
+
+void Class4::f() { HelperC2 c2; }
+
+void Class5::f() {
+ int Result = K1 + K2 + K3;
+ HelperFun4();
+}
+
+int Class6::f() {
+ int R = K4;
+ return R;
+}
+
+int Class7::f() {
+ int R = K6;
+ return R;
+}
+
+int Class7::g() {
+ HelperFun6();
+ return 1;
+}
+
+static int HelperFun5() {
+ int R = K5;
+ return R;
+}
+
+void Fun1() { HelperFun5(); }
+
+} // namespace a
+
+namespace b {
+namespace {
+void HelperFun7();
+
+class HelperC4;
+} // namespace
+
+void Fun3() {
+ HelperFun7();
+ HelperC4 *t;
+}
+
+namespace {
+void HelperFun7() {}
+
+class HelperC4 {};
+} // namespace
+} // namespace b
--- /dev/null
+namespace a {
+class Class1 {
+ void f();
+};
+
+class Class2 {
+ void f();
+};
+
+class Class3 {
+ void f();
+};
+
+class Class4 {
+ void f();
+};
+
+class Class5 {
+ void f();
+};
+
+class Class6 {
+ int f();
+};
+
+class Class7 {
+ int f();
+ int g();
+};
+
+void Fun1();
+
+inline void Fun2() {}
+
+} // namespace a
+
+namespace b {
+void Fun3();
+} // namespace b
--- /dev/null
+#include "macro_helper_test.h"
+
+#define DEFINE(name) \
+ namespace ns { \
+ static const bool t1 = false; \
+ bool t2_##name = t1; \
+ bool t3_##name = t1; \
+ } \
+ using ns::t2_##name;
+
+DEFINE(test)
+
+void f1() {}
--- /dev/null
+class A {};
+void f1();
--- /dev/null
+#include "multiple_class_test.h"
+
+using a::Move1;
+using namespace a;
+using A = a::Move1;
+static int g = 0;
+
+namespace a {
+int Move1::f() {
+ return 0;
+}
+} // namespace a
+
+namespace {
+using a::Move1;
+using namespace a;
+static int k = 0;
+} // namespace
+
+namespace b {
+using a::Move1;
+using namespace a;
+using T = a::Move1;
+int Move2::f() {
+ return 0;
+}
+} // namespace b
+
+namespace c {
+int Move3::f() {
+ using a::Move1;
+ using namespace b;
+ return 0;
+}
+
+int Move4::f() {
+ return k;
+}
+
+int EnclosingMove5::a = 1;
+
+int EnclosingMove5::Nested::f() {
+ return g;
+}
+
+int EnclosingMove5::Nested::b = 1;
+
+int NoMove::f() {
+ static int F = 0;
+ return g;
+}
+} // namespace c
--- /dev/null
+namespace a {
+class Move1 {
+public:
+ int f();
+};
+} // namespace a
+
+namespace b {
+class Move2 {
+public:
+ int f();
+};
+} // namespace b
+
+namespace c {
+class Move3 {
+public:
+ int f();
+};
+
+class Move4 {
+public:
+ int f();
+};
+
+class EnclosingMove5 {
+public:
+ class Nested {
+ int f();
+ static int b;
+ };
+ static int a;
+};
+
+class NoMove {
+public:
+ int f();
+};
+} // namespace c
--- /dev/null
+#include "template_class_test.h"
+
+template <typename T>
+void A<T>::g() {}
+
+template <typename T>
+template <typename U>
+void A<T>::k() {}
+
+template <typename T>
+int A<T>::c = 2;
+
+void B::f() {}
--- /dev/null
+#ifndef TEMPLATE_CLASS_TEST_H // comment 1
+#define TEMPLATE_CLASS_TEST_H
+
+template <typename T>
+class A {
+ public:
+ void f();
+ void g();
+ template <typename U> void h();
+ template <typename U> void k();
+ static int b;
+ static int c;
+};
+
+template <typename T>
+void A<T>::f() {}
+
+template <typename T>
+template <typename U>
+void A<T>::h() {}
+
+template <typename T>
+int A<T>::b = 2;
+
+class B {
+ public:
+ void f();
+};
+
+#endif // TEMPLATE_CLASS_TEST_H
--- /dev/null
+#include "test.h"
+#include "test2.h"
+
+namespace a {
+int Foo::f() {
+ return 0;
+}
+int Foo::f2(int a, int b) {
+ return a + b;
+}
+} // namespace a
--- /dev/null
+#ifndef TEST_H // comment 1
+#define TEST_H
+namespace a {
+class Foo {
+public:
+ int f();
+ int f2(int a, int b);
+};
+} // namespace a
+#endif // TEST_H
--- /dev/null
+typedef int Int1;
+using Int2 = int;
+
+template<class T>
+struct A {};
+
+template <class T> using B = A<T>;
+
+class C {
+ typedef int Int3;
+};
--- /dev/null
+#include "var_test.h"
+
+namespace a{
+int kGlobalInt = 1;
+const char *const kGlobalStr = "Hello";
+}
--- /dev/null
+namespace a {
+extern int kGlobalInt;
+extern const char *const kGlobalStr;
+}
+
+int kEvilInt = 2;
+
+inline void f1() {
+ int kGlobalInt = 3;
+ const char *const kGlobalStr = "Hello2";
+}
--- /dev/null
+// RUN: mkdir -p %T/clang-move/build
+// RUN: mkdir -p %T/clang-move/include
+// RUN: mkdir -p %T/clang-move/src
+// RUN: sed 's|$test_dir|%/T/clang-move|g' %S/Inputs/database_template.json > %T/clang-move/compile_commands.json
+// RUN: cp %S/Inputs/test.h %T/clang-move/include
+// RUN: cp %S/Inputs/test.cpp %T/clang-move/src
+// RUN: touch %T/clang-move/include/test2.h
+// RUN: cd %T/clang-move/build
+// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=../src/test.cpp -old_header=../include/test.h %T/clang-move/src/test.cpp
+// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+//
+// RUN: cp %S/Inputs/test.h %T/clang-move/include
+// RUN: cp %S/Inputs/test.cpp %T/clang-move/src
+// RUN: cd %T/clang-move/build
+// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=%T/clang-move/src/test.cpp -old_header=%T/clang-move/include/test.h %T/clang-move/src/test.cpp
+// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+//
+//
+// CHECK-NEW-TEST-H: #ifndef TEST_H // comment 1
+// CHECK-NEW-TEST-H: #define TEST_H
+// CHECK-NEW-TEST-H: namespace a {
+// CHECK-NEW-TEST-H: class Foo {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: int f2(int a, int b);
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace a
+// CHECK-NEW-TEST-H: #endif // TEST_H
+//
+// CHECK-NEW-TEST-CPP: #include "{{.*}}new_test.h"
+// CHECK-NEW-TEST-CPP: #include "test2.h"
+// CHECK-NEW-TEST-CPP: namespace a {
+// CHECK-NEW-TEST-CPP: int Foo::f() { return 0; }
+// CHECK-NEW-TEST-CPP: int Foo::f2(int a, int b) { return a + b; }
+// CHECK-NEW-TEST-CPP: } // namespace a
+//
+// CHECK-OLD-TEST-EMPTY: {{^}}{{$}}
--- /dev/null
+// RUN: mkdir -p %T/move-enum
+// RUN: cp %S/Inputs/enum.h %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: cd %T/move-enum
+//
+// -----------------------------------------------------------------------------
+// Test moving enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="a::E1" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-enum/enum.h -check-prefix=CHECK-OLD-TEST-H-CASE1 %s
+//
+// CHECK-NEW-TEST-H-CASE1: namespace a {
+// CHECK-NEW-TEST-H-CASE1-NEXT: enum E1 { Green, Red };
+// CHECK-NEW-TEST-H-CASE1-NEXT: }
+
+// CHECK-OLD-TEST-H-CASE1-NOT: enum E1 { Green, Red };
+
+
+// -----------------------------------------------------------------------------
+// Test moving scoped enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/enum.h %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: clang-move -names="a::E2" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-enum/enum.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+
+// CHECK-NEW-TEST-H-CASE2: namespace a {
+// CHECK-NEW-TEST-H-CASE2-NEXT: enum class E2 { Yellow };
+// CHECK-NEW-TEST-H-CASE2-NEXT: }
+
+// CHECK-OLD-TEST-H-CASE2-NOT: enum class E2 { Yellow };
+
+
+// -----------------------------------------------------------------------------
+// Test not moving class-insided enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/enum.h %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: clang-move -names="a::C::E3" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-EMPTY: {{^}}{{$}}
--- /dev/null
+// RUN: mkdir -p %T/move-function
+// RUN: cp %S/Inputs/function_test* %T/move-function
+// RUN: cd %T/move-function
+// RUN: clang-move -names="g" -new_header=%T/move-function/new_function_test.h -old_header=../move-function/function_test.h %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+//
+// CHECK-NEW-TEST-H-CASE1: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE1: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE1: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE1: inline int g() { return 0; }
+// CHECK-NEW-TEST-H-CASE1: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE1: #endif // {{.*}}NEW_FUNCTION_TEST_H
+//
+// RUN: cp %S/Inputs/function_test* %T/move-function
+// RUN: clang-move -names="h" -new_header=%T/move-function/new_function_test.h -old_header=../move-function/function_test.h %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+//
+// CHECK-NEW-TEST-H-CASE2: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE2: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> void h(T t) {}
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: template <> void h(int t) {}
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: #endif // {{.*}}NEW_FUNCTION_TEST_H
+//
+// RUN: cp %S/Inputs/function_test* %T/move-function
+// RUN: clang-move -names="f" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE3 %s
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE3 %s
+//
+// CHECK-NEW-TEST-H-CASE3: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE3: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE3: void f();
+// CHECK-NEW-TEST-H-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE3: #endif // {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-CPP-CASE3: #include "{{.*}}new_function_test.h"
+// CHECK-NEW-TEST-CPP-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE3: void f() {}
+//
+// RUN: cp %S/Inputs/function_test* %T/move-function
+// RUN: clang-move -names="A::f" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp -dump_result -- | FileCheck %s -check-prefix=CHECK-EMPTY
+//
+// CHECK-EMPTY: [{{[[:space:]]*}}]
+//
+// RUN: cp %S/Inputs/function_test* %T/move-function
+// RUN: clang-move -names="f,A" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE4 %s
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE4 %s
+
+// CHECK-NEW-TEST-H-CASE4: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE4: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: void f();
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: class A {
+// CHECK-NEW-TEST-H-CASE4: public:
+// CHECK-NEW-TEST-H-CASE4: void f();
+// CHECK-NEW-TEST-H-CASE4: };
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: #endif // {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-CPP-CASE4: #include "{{.*}}new_function_test.h"
+// CHECK-NEW-TEST-CPP-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE4: void f() {}
+// CHECK-NEW-TEST-CPP-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE4: void A::f() {}
--- /dev/null
+// RUN: mkdir -p %T/move-multiple-classes
+// RUN: cp %S/Inputs/multiple_class_test* %T/move-multiple-classes/
+// RUN: cd %T/move-multiple-classes
+// RUN: clang-move -names="c::EnclosingMove5::Nested" -new_cc=%T/move-multiple-classes/new_multiple_class_test.cpp -new_header=%T/move-multiple-classes/new_multiple_class_test.h -old_cc=%T/move-multiple-classes/multiple_class_test.cpp -old_header=../move-multiple-classes/multiple_class_test.h -dump_result %T/move-multiple-classes/multiple_class_test.cpp -- -std=c++11| FileCheck %s -check-prefix=CHECK-EMPTY
+// RUN: clang-move -names="a::Move1, b::Move2,c::Move3,c::Move4,c::EnclosingMove5" -new_cc=%T/move-multiple-classes/new_multiple_class_test.cpp -new_header=%T/move-multiple-classes/new_multiple_class_test.h -old_cc=%T/move-multiple-classes/multiple_class_test.cpp -old_header=../move-multiple-classes/multiple_class_test.h %T/move-multiple-classes/multiple_class_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-multiple-classes/new_multiple_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/new_multiple_class_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/multiple_class_test.cpp -check-prefix=CHECK-OLD-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/multiple_class_test.h -check-prefix=CHECK-OLD-TEST-H %s
+//
+// CHECK-EMPTY: [{{[[:space:]]*}}]
+//
+// CHECK-OLD-TEST-H: namespace c {
+// CHECK-OLD-TEST-H: class NoMove {
+// CHECK-OLD-TEST-H: public:
+// CHECK-OLD-TEST-H: int f();
+// CHECK-OLD-TEST-H: };
+// CHECK-OLD-TEST-H: } // namespace c
+
+// CHECK-OLD-TEST-CPP: #include "{{.*}}multiple_class_test.h"
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: using A = a::Move1;
+// CHECK-OLD-TEST-CPP: static int g = 0;
+// CHECK-OLD-TEST-CPP: namespace {
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: } // namespace
+// CHECK-OLD-TEST-CPP: namespace b {
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: using T = a::Move1;
+// CHECK-OLD-TEST-CPP: } // namespace b
+// CHECK-OLD-TEST-CPP: namespace c {
+// CHECK-OLD-TEST-CPP: int NoMove::f() {
+// CHECK-OLD-TEST-CPP: static int F = 0;
+// CHECK-OLD-TEST-CPP: return g;
+// CHECK-OLD-TEST-CPP: }
+// CHECK-OLD-TEST-CPP: } // namespace c
+
+// CHECK-NEW-TEST-H: #ifndef {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+// CHECK-NEW-TEST-H: #define {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+// CHECK-NEW-TEST-H: namespace a {
+// CHECK-NEW-TEST-H: class Move1 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace a
+// CHECK-NEW-TEST-H: namespace b {
+// CHECK-NEW-TEST-H: class Move2 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace b
+// CHECK-NEW-TEST-H: namespace c {
+// CHECK-NEW-TEST-H: class Move3 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: class Move4 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: class EnclosingMove5 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H: class Nested {
+// CHECK-NEW-TEST-H: int f();
+// CHECK-NEW-TEST-H: static int b;
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: static int a;
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace c
+// CHECK-NEW-TEST-H: #endif // {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+
+// CHECK-NEW-TEST-CPP: #include "{{.*}}new_multiple_class_test.h"
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: using A = a::Move1;
+// CHECK-NEW-TEST-CPP: static int g = 0;
+// CHECK-NEW-TEST-CPP: namespace a {
+// CHECK-NEW-TEST-CPP: int Move1::f() { return 0; }
+// CHECK-NEW-TEST-CPP: } // namespace a
+// CHECK-NEW-TEST-CPP: namespace {
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: static int k = 0;
+// CHECK-NEW-TEST-CPP: } // namespace
+// CHECK-NEW-TEST-CPP: namespace b {
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: using T = a::Move1;
+// CHECK-NEW-TEST-CPP: int Move2::f() { return 0; }
+// CHECK-NEW-TEST-CPP: } // namespace b
+// CHECK-NEW-TEST-CPP: namespace c {
+// CHECK-NEW-TEST-CPP: int Move3::f() {
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace b;
+// CHECK-NEW-TEST-CPP: return 0;
+// CHECK-NEW-TEST-CPP: }
+// CHECK-NEW-TEST-CPP: int Move4::f() { return k; }
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::a = 1;
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::f() { return g; }
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::b = 1;
+// CHECK-NEW-TEST-CPP: } // namespace c
--- /dev/null
+// RUN: mkdir -p %T/move-template-class
+// RUN: cp %S/Inputs/template_class_test* %T/move-template-class
+// RUN: cd %T/move-template-class
+// RUN: clang-move -names="A,B" -new_cc=%T/move-template-class/new_template_class_test.cpp -new_header=%T/move-template-class/new_template_class_test.h -old_cc=%T/move-template-class/template_class_test.cpp -old_header=../move-template-class/template_class_test.h %T/move-template-class/template_class_test.cpp --
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+//
+// RUN: cp %S/Inputs/template_class_test* %T/move-template-class
+// RUN: clang-move -names="A" -new_cc=%T/move-template-class/new_template_class_test.cpp -new_header=%T/move-template-class/new_template_class_test.h -old_cc=%T/move-template-class/template_class_test.cpp -old_header=../move-template-class/template_class_test.h %T/move-template-class/template_class_test.cpp --
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.cpp -check-prefix=CHECK-OLD-TEST-CPP-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE2 %s
+//
+//
+// CHECK-OLD-TEST-EMPTY: {{^}}{{$}}
+//
+// CHECK-NEW-TEST-H-CASE1: #ifndef TEMPLATE_CLASS_TEST_H // comment 1
+// CHECK-NEW-TEST-H-CASE1: #define TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: class A {
+// CHECK-NEW-TEST-H-CASE1: public:
+// CHECK-NEW-TEST-H-CASE1: void f();
+// CHECK-NEW-TEST-H-CASE1: void g();
+// CHECK-NEW-TEST-H-CASE1: template <typename U> void h();
+// CHECK-NEW-TEST-H-CASE1: template <typename U> void k();
+// CHECK-NEW-TEST-H-CASE1: static int b;
+// CHECK-NEW-TEST-H-CASE1: static int c;
+// CHECK-NEW-TEST-H-CASE1: };
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: void A<T>::f() {}
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: template <typename U>
+// CHECK-NEW-TEST-H-CASE1: void A<T>::h() {}
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: int A<T>::b = 2;
+// CHECK-NEW-TEST-H-CASE1: class B {
+// CHECK-NEW-TEST-H-CASE1: public:
+// CHECK-NEW-TEST-H-CASE1: void f();
+// CHECK-NEW-TEST-H-CASE1: };
+// CHECK-NEW-TEST-H-CASE1: #endif // TEMPLATE_CLASS_TEST_H
+//
+// CHECK-NEW-TEST-CPP-CASE1: #include "{{.*}}new_template_class_test.h"
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: void A<T>::g() {}
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: template <typename U>
+// CHECK-NEW-TEST-CPP-CASE1: void A<T>::k() {}
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: int A<T>::c = 2;
+// CHECK-NEW-TEST-CPP-CASE1: void B::f() {}
+//
+// CHECK-OLD-TEST-H-CASE2: #ifndef TEMPLATE_CLASS_TEST_H // comment 1
+// CHECK-OLD-TEST-H-CASE2: #define TEMPLATE_CLASS_TEST_H
+// CHECK-OLD-TEST-H-CASE2: class B {
+// CHECK-OLD-TEST-H-CASE2: public:
+// CHECK-OLD-TEST-H-CASE2: void f();
+// CHECK-OLD-TEST-H-CASE2: };
+// CHECK-OLD-TEST-H-CASE2: #endif // TEMPLATE_CLASS_TEST_H
+//
+// CHECK-OLD-TEST-CPP-CASE2: #include "template_class_test.h"
+// CHECK-OLD-TEST-CPP-CASE2: void B::f() {}
+//
+// CHECK-NEW-TEST-H-CASE2: #ifndef {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE2: #define {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE2: template <typename T>
+// CHECK-NEW-TEST-H-CASE2: class A {
+// CHECK-NEW-TEST-H-CASE2: public:
+// CHECK-NEW-TEST-H-CASE2: void f();
+// CHECK-NEW-TEST-H-CASE2: void g();
+// CHECK-NEW-TEST-H-CASE2: template <typename U> void h();
+// CHECK-NEW-TEST-H-CASE2: template <typename U> void k();
+// CHECK-NEW-TEST-H-CASE2: static int b;
+// CHECK-NEW-TEST-H-CASE2: static int c;
+// CHECK-NEW-TEST-H-CASE2: };
+// CHECK-NEW-TEST-H-CASE2: template <typename T> void A<T>::f() {}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> template <typename U> void A<T>::h() {}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> int A<T>::b = 2;
+// CHECK-NEW-TEST-H-CASE2: #endif // {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+//
+// CHECK-NEW-TEST-CPP-CASE2: #include "{{.*}}new_template_class_test.h"
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> void A<T>::g() {}
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> template <typename U> void A<T>::k() {}
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> int A<T>::c = 2;
--- /dev/null
+// RUN: mkdir -p %T/move-type-alias
+// RUN: cp %S/Inputs/type_alias.h %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: cd %T/move-type-alias
+//
+// -----------------------------------------------------------------------------
+// Test moving typedef declarations.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="Int1" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-type-alias/type_alias.h -check-prefix=CHECK-OLD-TEST-H-CASE1 %s
+
+// CHECK-NEW-TEST-H-CASE1: typedef int Int1;
+
+// CHECK-OLD-TEST-H-CASE1-NOT: typedef int Int1;
+
+
+// -----------------------------------------------------------------------------
+// Test moving type alias declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="Int2" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-type-alias/type_alias.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+
+// CHECK-NEW-TEST-H-CASE2: using Int2 = int;
+
+// CHECK-OLD-TEST-H-CASE2-NOT: using Int2 = int;
+
+
+// -----------------------------------------------------------------------------
+// Test moving template type alias declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="B" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-OLD-TEST-H-CASE3 %s
+
+// CHECK-NEW-TEST-H-CASE3: template<class T> using B = A<T>;
+// CHECK-OLD-TEST-H-CASE3-NOT: template<class T> using B = A<T>;
+
+
+// -----------------------------------------------------------------------------
+// Test not moving class-insided typedef declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="C::Int3" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-EMPTY: {{^}}{{$}}
--- /dev/null
+// RUN: mkdir -p %T/used-helper-decls
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: cd %T/used-helper-decls
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used functions.
+// ----------------------------------------------------------------------------
+// RUN: clang-move -names="a::Class1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS1-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS1-CPP %s
+
+// CHECK-NEW-CLASS1-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun2() { HelperFun1(); }
+// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS1-CPP-NEXT: void Class1::f() { HelperFun2(); }
+// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace a
+//
+// CHECK-OLD-CLASS1-CPP: void HelperFun1() {}
+// CHECK-OLD-CLASS1-CPP-NOT: void HelperFun2() { HelperFun1(); }
+// CHECK-OLD-CLASS1-CPP-NOT: void Class1::f() { HelperFun2(); }
+// CHECK-OLD-CLASS1-CPP: void Class2::f() {
+// CHECK-OLD-CLASS1-CPP: HelperFun1();
+
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used static variables.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS2-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS2-CPP %s
+
+// CHECK-NEW-CLASS2-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS2-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: static void HelperFun3() { K2; }
+// CHECK-NEW-CLASS2-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS2-CPP-NEXT: void Class2::f() {
+// CHECK-NEW-CLASS2-CPP-NEXT: HelperFun1();
+// CHECK-NEW-CLASS2-CPP-NEXT: HelperFun3();
+// CHECK-NEW-CLASS2-CPP-NEXT: }
+// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS2-CPP: void HelperFun1() {}
+// CHECK-OLD-CLASS2-CPP: void HelperFun2() { HelperFun1(); }
+// CHECK-OLD-CLASS2-CPP: const int K1 = 1;
+// CHECK-OLD-CLASS2-CPP: static const int K2 = 2;
+// CHECK-OLD-CLASS2-CPP-NOT: static void HelperFun3() { K2; }
+// CHECK-OLD-CLASS2-CPP-NOT: void Class2::f() {
+// CHECK-OLD-CLASS2-CPP-NOT: HelperFun1();
+// CHECK-OLD-CLASS2-CPP-NOT: HelperFun3();
+// CHECK-OLD-CLASS2-CPP: void Class5::f() {
+// CHECK-OLD-CLASS2-CPP-NEXT: int Result = K1 + K2 + K3;
+
+
+// ----------------------------------------------------------------------------
+// Test using a static member variable of a helper class.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS3-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS3-CPP %s
+
+// CHECK-NEW-CLASS3-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS3-CPP-NEXT: class HelperC1 {
+// CHECK-NEW-CLASS3-CPP-NEXT: public:
+// CHECK-NEW-CLASS3-CPP-NEXT: static int I;
+// CHECK-NEW-CLASS3-CPP-NEXT: };
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: int HelperC1::I = 0;
+// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS3-CPP-NEXT: void Class3::f() { HelperC1::I; }
+// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS3-CPP: namespace {
+// CHECK-OLD-CLASS3-CPP-NOT: class HelperC1 {
+// CHECK-OLD-CLASS3-CPP-NOT: public:
+// CHECK-OLD-CLASS3-CPP-NOT: static int I;
+// CHECK-OLD-CLASS3-CPP-NOT: };
+// CHECK-OLD-CLASS3-CPP-NOT: int HelperC1::I = 0;
+// CHECK-OLD-CLASS3-CPP: class HelperC2 {};
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper classes.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class4" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS4-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS4-CPP %s
+
+// CHECK-NEW-CLASS4-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS4-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS4-CPP-NEXT: class HelperC2 {};
+// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS4-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS4-CPP-NEXT: void Class4::f() { HelperC2 c2; }
+// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS4-CPP-NOT: class HelperC2 {};
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper variables and helper functions together.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class5" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS5-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS5-CPP %s
+
+// CHECK-NEW-CLASS5-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS5-CPP-NEXT: const int K1 = 1;
+// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CLASS5-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS5-CPP-NEXT: static const int K3 = 3;
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: static void HelperFun4() {}
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: void Class5::f() {
+// CHECK-NEW-CLASS5-CPP-NEXT: int Result = K1 + K2 + K3;
+// CHECK-NEW-CLASS5-CPP-NEXT: HelperFun4();
+// CHECK-NEW-CLASS5-CPP-NEXT: }
+// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS5-CPP-NOT: const int K1 = 1;
+// CHECK-OLD-CLASS5-CPP: static const int K2 = 2;
+// CHECK-OLD-CLASS5-CPP: static void HelperFun3() { K2; }
+// CHECK-OLD-CLASS5-CPP: static const int K4 = HelperC3::I;
+// CHECK-OLD-CLASS5-CPP-NOT: void Class5::f() {
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper variables and their transively used helper classes.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class6" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS6-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS6-CPP %s
+
+// CHECK-NEW-CLASS6-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS6-CPP-NEXT: class HelperC3 {
+// CHECK-NEW-CLASS6-CPP-NEXT: public:
+// CHECK-NEW-CLASS6-CPP-NEXT: static int I;
+// CHECK-NEW-CLASS6-CPP-NEXT: };
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: int HelperC3::I = 0;
+// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS6-CPP-NEXT: static const int K4 = HelperC3::I;
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: int Class6::f() {
+// CHECK-NEW-CLASS6-CPP-NEXT: int R = K4;
+// CHECK-NEW-CLASS6-CPP-NEXT: return R;
+// CHECK-NEW-CLASS6-CPP-NEXT: }
+// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS6-CPP-NOT: class HelperC3 {
+// CHECK-OLD-CLASS6-CPP-NOT: int HelperC3::I = 0;
+// CHECK-OLD-CLASS6-CPP-NOT: static const int K4 = HelperC3::I;
+
+
+// ----------------------------------------------------------------------------
+// Test moving classes where its methods use helpers.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class7" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS7-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS7-CPP %s
+
+// CHECK-NEW-CLASS7-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS7-CPP-NEXT: static const int K6 = 6;
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: static void HelperFun6() {}
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: int Class7::f() {
+// CHECK-NEW-CLASS7-CPP-NEXT: int R = K6;
+// CHECK-NEW-CLASS7-CPP-NEXT: return R;
+// CHECK-NEW-CLASS7-CPP-NEXT: }
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: int Class7::g() {
+// CHECK-NEW-CLASS7-CPP-NEXT: HelperFun6();
+// CHECK-NEW-CLASS7-CPP-NEXT: return 1;
+// CHECK-NEW-CLASS7-CPP-NEXT: }
+// CHECK-NEW-CLASS7-CPP-NEXT: } // namespace a
+//
+// CHECK-OLD-CLASS7-CPP-NOT: static const int K6 = 6;
+// CHECK-OLD-CLASS7-CPP-NOT: static void HelperFun6() {}
+// CHECK-OLD-CLASS7-CPP-NOT: int Class7::f() {
+// CHECK-OLD-CLASS7-CPP-NOT: int Class7::g() {
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper function and its transively used helper variables.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Fun1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN1-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-FUN1-CPP %s
+
+// CHECK-NEW-FUN1-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: namespace a {
+// CHECK-NEW-FUN1-CPP-NEXT: static const int K5 = 5;
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: static int HelperFun5() {
+// CHECK-NEW-FUN1-CPP-NEXT: int R = K5;
+// CHECK-NEW-FUN1-CPP-NEXT: return R;
+// CHECK-NEW-FUN1-CPP-NEXT: }
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: void Fun1() { HelperFun5(); }
+// CHECK-NEW-FUN1-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-FUN1-CPP-NOT: static const int K5 = 5;
+// CHECK-OLD-FUN1-CPP-NOT: static int HelperFun5() {
+// CHECK-OLD-FUN1-CPP-NOT: void Fun1() { HelperFun5(); }
+
+
+// ----------------------------------------------------------------------------
+// Test no moving helpers when moving inline functions in header.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Fun2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN2-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.h -check-prefix=CHECK-NEW-FUN2-H %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.h -check-prefix=CHECK-OLD-FUN2-H %s
+
+// CHECK-NEW-FUN2-H: namespace a {
+// CHECK-NEW-FUN2-H-NEXT: inline void Fun2() {}
+// CHECK-NEW-FUN2-H-NEXT: } // namespace a
+
+// CHECK-NEW-FUN2-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN2-CPP-SAME: {{[[:space:]]}}
+
+// CHECK-OLD-FUN2-H-NOT: inline void Fun2() {}
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used functions.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="b::Fun3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN3-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-FUN3-CPP %s
+
+// CHECK-NEW-FUN3-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: namespace b {
+// CHECK-NEW-FUN3-CPP-NEXT: namespace {
+// CHECK-NEW-FUN3-CPP-NEXT: void HelperFun7();
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: class HelperC4;
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: void Fun3() {
+// CHECK-NEW-FUN3-CPP-NEXT: HelperFun7();
+// CHECK-NEW-FUN3-CPP-NEXT: HelperC4 *t;
+// CHECK-NEW-FUN3-CPP-NEXT: }
+// CHECK-NEW-FUN3-CPP-NEXT: namespace {
+// CHECK-NEW-FUN3-CPP-NEXT: void HelperFun7() {}
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: class HelperC4 {};
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace b
+//
+// CHECK-OLD-FUN3-CPP-NOT: void HelperFun7();
+// CHECK-OLD-FUN3-CPP-NOT: void HelperFun7() {}
+// CHECK-OLD-FUN3-CPP-NOT: void Fun3() { HelperFun7(); }
+
+// ----------------------------------------------------------------------------
+// Test moving all symbols in headers.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test* %T/used-helper-decls/
+// RUN: clang-move -names="a::Class1, a::Class2, a::Class3, a::Class4, a::Class5, a::Class5, a::Class6, a::Class7, a::Fun1, a::Fun2, b::Fun3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.h -check-prefix=CHECK-NEW-H %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -allow-empty -check-prefix=CHECK-EMPTY %s
+
+
+// CHECK-NEW-H: namespace a {
+// CHECK-NEW-H-NEXT: class Class1 {
+// CHECK-NEW-H-NEXT: void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class2 {
+// CHECK-NEW-H-NEXT: void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class3 {
+// CHECK-NEW-H-NEXT: void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class4 {
+// CHECK-NEW-H-NEXT: void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class5 {
+// CHECK-NEW-H-NEXT: void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class6 {
+// CHECK-NEW-H-NEXT: int f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class7 {
+// CHECK-NEW-H-NEXT: int f();
+// CHECK-NEW-H-NEXT: int g();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: void Fun1();
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: inline void Fun2() {}
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: } // namespace a
+
+
+// CHECK-NEW-CPP: namespace {
+// CHECK-NEW-CPP-NEXT: class HelperC1 {
+// CHECK-NEW-CPP-NEXT: public:
+// CHECK-NEW-CPP-NEXT: static int I;
+// CHECK-NEW-CPP-NEXT: };
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int HelperC1::I = 0;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC2 {};
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC3 {
+// CHECK-NEW-CPP-NEXT: public:
+// CHECK-NEW-CPP-NEXT: static int I;
+// CHECK-NEW-CPP-NEXT: };
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int HelperC3::I = 0;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void HelperFun2() { HelperFun1(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: const int K1 = 1;
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CPP-NEXT: static void HelperFun3() { K2; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace a {
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static const int K3 = 3;
+// CHECK-NEW-CPP-NEXT: static const int K4 = HelperC3::I;
+// CHECK-NEW-CPP-NEXT: static const int K5 = 5;
+// CHECK-NEW-CPP-NEXT: static const int K6 = 6;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static void HelperFun4() {}
+// CHECK-NEW-CPP-NEXT: static void HelperFun6() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class1::f() { HelperFun2(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class2::f() {
+// CHECK-NEW-CPP-NEXT: HelperFun1();
+// CHECK-NEW-CPP-NEXT: HelperFun3();
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class3::f() { HelperC1::I; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class4::f() { HelperC2 c2; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class5::f() {
+// CHECK-NEW-CPP-NEXT: int Result = K1 + K2 + K3;
+// CHECK-NEW-CPP-NEXT: HelperFun4();
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class6::f() {
+// CHECK-NEW-CPP-NEXT: int R = K4;
+// CHECK-NEW-CPP-NEXT: return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class7::f() {
+// CHECK-NEW-CPP-NEXT: int R = K6;
+// CHECK-NEW-CPP-NEXT: return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class7::g() {
+// CHECK-NEW-CPP-NEXT: HelperFun6();
+// CHECK-NEW-CPP-NEXT: return 1;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static int HelperFun5() {
+// CHECK-NEW-CPP-NEXT: int R = K5;
+// CHECK-NEW-CPP-NEXT: return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Fun1() { HelperFun5(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: } // namespace a
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace b {
+// CHECK-NEW-CPP-NEXT: namespace {
+// CHECK-NEW-CPP-NEXT: void HelperFun7();
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC4;
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Fun3() {
+// CHECK-NEW-CPP-NEXT: HelperFun7();
+// CHECK-NEW-CPP-NEXT: HelperC4 *t;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace {
+// CHECK-NEW-CPP-NEXT: void HelperFun7() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC4 {};
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-NEXT: } // namespace b
+
+// CHECK-EMPTY: {{^}}{{$}}
--- /dev/null
+// RUN: mkdir -p %T/move-var
+// RUN: cp %S/Inputs/var_test* %T/move-var
+// RUN: cd %T/move-var
+// RUN: clang-move -names="a::kGlobalInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE1 %s
+
+// CHECK-OLD-VAR-H-CASE1-NOT: extern int kGlobalInt;
+// CHECK-OLD-VAR-H-CASE1: int kGlobalInt = 3;
+
+// CHECK-OLD-VAR-CPP-CASE1-NOT: int kGlobalInt = 1;
+
+// CHECK-NEW-VAR-H-CASE1: extern int kGlobalInt;
+// CHECK-NEW-VAR-H-CASE1-NOT: int kGlobalInt = 3;
+
+// CHECK-NEW-VAR-CPP-CASE1: int kGlobalInt = 1;
+
+
+// RUN: cp %S/Inputs/var_test* %T/move-var
+// RUN: clang-move -names="a::kGlobalStr" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE2 %s
+
+// CHECK-OLD-VAR-H-CASE2-NOT: extern const char *const kGlobalStr;
+// CHECK-OLD-VAR-H-CASE2: const char *const kGlobalStr = "Hello2";
+
+// CHECK-OLD-VAR-CPP-CASE2-NOT: const char *const kGlobalStr = "Hello";
+
+// CHECK-NEW-VAR-H-CASE2: extern const char *const kGlobalStr;
+// CHECK-NEW-VAR-H-CASE2-NOT: const char *const kGlobalStr = "Hello2";
+
+// CHECK-NEW-VAR-CPP-CASE2: const char *const kGlobalStr = "Hello";
+
+
+// RUN: cp %S/Inputs/var_test* %T/move-var
+// RUN: clang-move -names="kEvilInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE3 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE3 %s
+
+// CHECK-OLD-VAR-H-CASE3-NOT: int kEvilInt = 2;
+
+// CHECK-NEW-VAR-H-CASE3: int kEvilInt = 2;
--- /dev/null
+// RUN: mkdir -p %T/no-move-macro-helper
+// RUN: cp %S/Inputs/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.h
+// RUN: cp %S/Inputs/macro_helper_test.cpp %T/no-move-macro-helper/macro_helper_test.cpp
+// RUN: cd %T/no-move-macro-helper
+//
+// -----------------------------------------------------------------------------
+// Test no moving helpers in macro.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="A" -new_cc=%T/no-move-macro-helper/new_test.cpp -new_header=%T/no-move-macro-helper/new_test.h -old_cc=%T/no-move-macro-helper/macro_helper_test.cpp -old_header=%T/no-move-macro-helper/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.h -check-prefix=CHECK-NEW-TEST-CASE1-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.cpp -check-prefix=CHECK-NEW-TEST-CASE1-CPP %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.h -check-prefix=CHECK-OLD-TEST-CASE1-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.cpp -check-prefix=CHECK-OLD-TEST-CASE1-CPP %s
+
+// CHECK-NEW-TEST-CASE1-H: class A {};
+
+// CHECK-OLD-TEST-CASE1-H-NOT: class A {};
+
+// CHECK-OLD-TEST-CASE1-CPP: DEFINE(test)
+
+// CHECK-NEW-TEST-CASE1-CPP-NOT: DEFINE(test)
+
+
+// -----------------------------------------------------------------------------
+// Test moving all.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.h
+// RUN: cp %S/Inputs/macro_helper_test.cpp %T/no-move-macro-helper/macro_helper_test.cpp
+// RUN: clang-move -names="A, f1" -new_cc=%T/no-move-macro-helper/new_test.cpp -new_header=%T/no-move-macro-helper/new_test.h -old_cc=%T/no-move-macro-helper/macro_helper_test.cpp -old_header=%T/no-move-macro-helper/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.cpp -- -std=c++11
+//
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.h -check-prefix=CHECK-NEW-TEST-CASE2-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.cpp -check-prefix=CHECK-NEW-TEST-CASE2-CPP %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.cpp -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-NEW-TEST-CASE2-H: class A {};
+// CHECK-NEW-TEST-CASE2-H-NEXT:void f1();
+
+
+// CHECK-NEW-TEST-CASE2-CPP: DEFINE(test)
+// CHECK-NEW-TEST-CASE2-CPP: void f1() {}
+
+// CHECK-EMPTY: {{^}}{{$}}
--- /dev/null
+// RUN: not clang-query -c foo -c bar %s -- | FileCheck %s
+// RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s
+// RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s
+// RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s
+
+// CHECK: unknown command: foo
+// CHECK-NOT: unknown command: bar
+
+// CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script
+// CHECK-BOTH: cannot specify both -c and -f
--- /dev/null
+// RUN: clang-query -c "match functionDecl()" %s -- | FileCheck %s
+
+// CHECK: function-decl.c:4:1: note: "root" binds here
+void foo(void) {}
--- /dev/null
+// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
+
+// The order of fields should not change.
+class Foo {
+public:
+ int x; // CHECK: {{^ int x;}}
+ int y; // CHECK-NEXT: {{^ int y;}}
+ int z; // CHECK-NEXT: {{^ int z;}}
+};
+
+int main() {
+ Foo foo = { 0, 1 }; // CHECK: {{^ Foo foo = { 0, 1 };}}
+ return 0;
+}
--- /dev/null
+// RUN: clang-reorder-fields -record-name ::Foo -fields-order y,x %s -- | FileCheck %s
+
+struct Foo {
+ int x; // CHECK: {{^ double y;}}
+ double y; // CHECK-NEXT: {{^ int x;}}
+};
+
+namespace bar {
+struct Foo {
+ int x; // CHECK: {{^ int x;}}
+ double y; // CHECK-NEXT: {{^ double y;}}
+};
+} // end namespace bar
+
+int main() {
+ bar::Foo foo = { 1, 1.7 }; // CHECK: {{^ bar::Foo foo = { 1, 1.7 };}}
+ return 0;
+}
--- /dev/null
+// RUN: clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x %s -- | FileCheck %s
+
+namespace bar {
+struct Foo {
+ const int* x; // CHECK: {{^ double z;}}
+ int y; // CHECK-NEXT: {{^ int w;}}
+ double z; // CHECK-NEXT: {{^ int y;}}
+ int w; // CHECK-NEXT: {{^ const int\* x}}
+};
+} // end namespace bar
+
+int main() {
+ const int x = 13;
+ bar::Foo foo = { &x, 0, 1.29, 17 }; // CHECK: {{^ bar::Foo foo = { 1.29, 17, 0, &x };}}
+ return 0;
+}
--- /dev/null
+// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
+
+// The order of fields should not change.
+class Foo {
+public:
+ int x; // CHECK: {{^ int x;}}
+
+private:
+ int y; // CHECK: {{^ int y;}}
+ int z; // CHECK-NEXT: {{^ int z;}}
+};
+
+int main() {
+ Foo foo;
+ return 0;
+}
--- /dev/null
+// RUN: clang-reorder-fields -record-name Foo -fields-order e,x,pi,s2,s1 %s -- -std=c++11 | FileCheck %s
+
+class Foo {
+public:
+ Foo();
+
+private:
+ int x; // CHECK: {{^ double e = 2.71;}}
+ const char *s1; // CHECK-NEXT: {{^ int x;}}
+ const char *s2; // CHECK-NEXT: {{^ double pi = 3.14;}}
+ double pi = 3.14; // CHECK-NEXT: {{^ const char \*s2;}}
+ double e = 2.71; // CHECK-NEXT: {{^ const char \*s1;}}
+};
+
+Foo::Foo():
+ x(12), // CHECK: {{^ x\(12\)}},
+ s1("abc"), // CHECK-NEXT: {{^ s2\("def"\)}},
+ s2("def") // CHECK-NEXT: {{^ s1\("abc"\)}}
+{}
+
+int main() {
+ Foo foo;
+ return 0;
+}
--- /dev/null
+// RUN: clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 %s -- | FileCheck %s
+
+class Foo {
+public:
+ Foo();
+
+private:
+ int x; // CHECK: {{^ const char \*s1;}}
+ const char *s1; // CHECK-NEXT: {{^ int x;}}
+ const char *s2; // CHECK-NEXT: {{^ double z;}}
+ double z; // CHECK-NEXT: {{^ const char \*s2;}}
+};
+
+Foo::Foo():
+ x(12), // CHECK: {{^ s1\("abc"\),}}
+ s1("abc"), // CHECK-NEXT: {{^ x\(12\),}}
+ s2("def"), // CHECK-NEXT: {{^ z\(3.14\),}}
+ z(3.14) // CHECK-NEXT: {{^ s2\("def"\)}}
+{}
+
+int main() {
+ Foo foo;
+ return 0;
+}
--- /dev/null
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// The line number of the following include should match the location of the
+// corresponding comment in llvm-include-order.cpp
+#include "cross-file-b.h"
--- /dev/null
+#pragma clang system_header
+
+namespace std {
+
+template<class T, T v>
+struct integral_constant {
+ static constexpr T value = v;
+ typedef T value_type;
+ typedef integral_constant type;
+ constexpr operator value_type() const noexcept { return value; }
+};
+
+template <bool B>
+using bool_constant = integral_constant<bool, B>;
+using true_type = bool_constant<true>;
+using false_type = bool_constant<false>;
+
+template<class T>
+struct is_error_code_enum : false_type {};
+
+template<class T>
+void swap(T &a, T &b);
+}
+
--- /dev/null
+[
+{
+ "directory": "test_dir/a",
+ "command": "clang++ -o test.o test_dir/a/a.cpp",
+ "file": "test_dir/a/a.cpp"
+},
+{
+ "directory": "test_dir/a",
+ "command": "clang++ -o test.o test_dir/a/b.cpp",
+ "file": "test_dir/a/b.cpp"
+},
+{
+ "directory": "test_dir/",
+ "command": "clang++ -o test.o test_dir/b/b.cpp",
+ "file": "test_dir/b/b.cpp"
+},
+{
+ "directory": "test_dir/b",
+ "command": "clang++ -o test.o ../b/c.cpp",
+ "file": "test_dir/b/c.cpp"
+},
+{
+ "directory": "test_dir/b",
+ "command": "clang++ -I../include -o test.o ../b/d.cpp",
+ "file": "test_dir/b/d.cpp"
+},
+{
+ "directory": "test_dir/",
+ "command": "clang++ -o test.o test_dir/b/not-exist.cpp",
+ "file": "test_dir/b/not-exist.cpp"
+}
+]
--- /dev/null
+Checks: 'from-parent'
+HeaderFilterRegex: 'parent'
--- /dev/null
+Checks: 'from-child1'
+HeaderFilterRegex: 'child1'
--- /dev/null
+Checks: '-*,modernize-use-nullptr'
--- /dev/null
+class A1 { A1(int); };
--- /dev/null
+class A2 { A2(int); };
--- /dev/null
+class A0 { A0(int); };
--- /dev/null
+namespace {
+int x;
+}
+
+namespace spaaaace {
+class core;
+}
--- /dev/null
+class A1 { A1(int); };
+class B1 { B1(int); };
+class C1 { C1(int); };
--- /dev/null
+class A2 { A2(int); };
+class B2 { B2(int); };
+class C2 { C2(int); };
--- /dev/null
+class A3 { A3(int); };
--- /dev/null
+#ifndef STRUCTURES_H
+#define STRUCTURES_H
+
+extern "C" {
+extern int printf(const char *restrict, ...);
+}
+
+struct Val {int X; void g(); };
+
+struct MutableVal {
+ void constFun(int) const;
+ void nonConstFun(int, int);
+ void constFun(MutableVal &) const;
+ void constParamFun(const MutableVal &) const;
+ void nonConstParamFun(const MutableVal &);
+ int X;
+};
+
+struct NonTriviallyCopyable {
+ NonTriviallyCopyable() = default;
+ // Define this constructor to make this class non-trivially copyable.
+ NonTriviallyCopyable(const NonTriviallyCopyable& Ntc);
+ int X;
+};
+
+struct TriviallyCopyableButBig {
+ int X;
+ char Array[16];
+};
+
+struct S {
+ typedef MutableVal *iterator;
+ typedef const MutableVal *const_iterator;
+ const_iterator begin() const;
+ const_iterator end() const;
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+ iterator begin();
+ iterator end();
+};
+
+struct T {
+ struct iterator {
+ int& operator*();
+ const int& operator*()const;
+ iterator& operator ++();
+ bool operator!=(const iterator &other);
+ void insert(int);
+ int X;
+ };
+ iterator begin();
+ iterator end();
+};
+
+struct U {
+ struct iterator {
+ Val& operator*();
+ const Val& operator*()const;
+ iterator& operator ++();
+ bool operator!=(const iterator &other);
+ Val *operator->();
+ };
+ iterator begin();
+ iterator end();
+ int X;
+};
+
+struct X {
+ S Ss;
+ T Tt;
+ U Uu;
+ S getS();
+};
+
+template<typename ElemType>
+class dependent {
+ public:
+ dependent<ElemType>();
+ struct iterator_base {
+ const ElemType& operator*()const;
+ iterator_base& operator ++();
+ bool operator!=(const iterator_base &other) const;
+ const ElemType *operator->() const;
+ };
+
+ struct iterator : iterator_base {
+ ElemType& operator*();
+ iterator& operator ++();
+ ElemType *operator->();
+ };
+
+ typedef iterator_base const_iterator;
+ const_iterator begin() const;
+ const_iterator end() const;
+ iterator begin();
+ iterator end();
+ unsigned size() const;
+ ElemType & operator[](unsigned);
+ const ElemType & operator[](unsigned) const;
+ ElemType & at(unsigned);
+ ElemType & at(unsigned, unsigned);
+ const ElemType & at(unsigned) const;
+
+ // Intentionally evil.
+ dependent<ElemType> operator*();
+
+ void foo();
+ void constFoo() const;
+};
+
+template<typename First, typename Second>
+class doublyDependent{
+ public:
+ struct Value {
+ First first;
+ Second second;
+ };
+
+ struct iterator_base {
+ const Value& operator*()const;
+ iterator_base& operator ++();
+ bool operator!=(const iterator_base &other) const;
+ const Value *operator->() const;
+ };
+
+ struct iterator : iterator_base {
+ Value& operator*();
+ Value& operator ++();
+ Value *operator->();
+ };
+
+ typedef iterator_base const_iterator;
+ const_iterator begin() const;
+ const_iterator end() const;
+ iterator begin();
+ iterator end();
+};
+
+template<typename Contained>
+class transparent {
+ public:
+ Contained *at();
+ Contained *operator->();
+ Contained operator*();
+};
+
+template<typename IteratorType>
+struct Nested {
+ typedef IteratorType* iterator;
+ typedef const IteratorType* const_iterator;
+ IteratorType *operator->();
+ IteratorType operator*();
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+};
+
+// Like llvm::SmallPtrSet, the iterator has a dereference operator that returns
+// by value instead of by reference.
+template <typename T>
+struct PtrSet {
+ struct iterator {
+ bool operator!=(const iterator &other) const;
+ const T operator*();
+ iterator &operator++();
+ };
+ iterator begin() const;
+ iterator end() const;
+};
+
+template <typename T>
+struct TypedefDerefContainer {
+ struct iterator {
+ typedef T &deref_type;
+ bool operator!=(const iterator &other) const;
+ deref_type operator*();
+ iterator &operator++();
+ };
+ iterator begin() const;
+ iterator end() const;
+};
+
+template <typename T>
+struct RValueDerefContainer {
+ struct iterator {
+ typedef T &&deref_type;
+ bool operator!=(const iterator &other) const;
+ deref_type operator*();
+ iterator &operator++();
+ };
+ iterator begin() const;
+ iterator end() const;
+};
+
+namespace Macros {
+
+struct MacroStruct {
+ int Arr[10];
+};
+static MacroStruct *MacroSt;
+#define CONT MacroSt->
+
+} // namespace Macros
+
+#endif // STRUCTURES_H
--- /dev/null
+struct S {
+ S(S&&);
+ S(const S&);
+};
+struct Foo {
+ Foo(const S &s);
+ S s;
+};
--- /dev/null
+class ThreadId {
+public:
+ ThreadId(const ThreadId &) {}
+ ThreadId(ThreadId &&) {}
+};
+
+struct A {
+ A(const ThreadId &tid) : threadid(tid) {}
+ ThreadId threadid;
+};
--- /dev/null
+#ifndef INPUTS_MEMORY_H
+#define INPUTS_MEMORY_H
+
+namespace std {
+
+inline namespace _1 {
+
+template <class Y> struct auto_ptr_ref {
+ Y *y_;
+};
+
+template <class X> class auto_ptr {
+public:
+ typedef X element_type;
+ explicit auto_ptr(X *p = 0) throw() {}
+ auto_ptr(auto_ptr &) throw() {}
+ template <class Y> auto_ptr(auto_ptr<Y> &) throw() {}
+ auto_ptr &operator=(auto_ptr &) throw() { return *this; }
+ template <class Y> auto_ptr &operator=(auto_ptr<Y> &) throw() {
+ return *this;
+ }
+ auto_ptr &operator=(auto_ptr_ref<X> r) throw() { return *this; }
+ ~auto_ptr() throw() {}
+ auto_ptr(auto_ptr_ref<X> r) throw() : x_(r.y_) {}
+ template <class Y> operator auto_ptr_ref<Y>() throw() {
+ auto_ptr_ref<Y> r;
+ r.y_ = x_;
+ return r;
+ }
+ template <class Y> operator auto_ptr<Y>() throw() { return auto_ptr<Y>(x_); }
+
+private:
+ X *x_;
+};
+
+template <> class auto_ptr<void> {
+public:
+ typedef void element_type;
+};
+
+} // namespace _1
+
+} // end namespace std
+
+#endif // INPUTS_MEMORY_H
--- /dev/null
+namespace std {
+
+template <typename type>
+class shared_ptr {
+public:
+ shared_ptr();
+ shared_ptr(type *ptr);
+ shared_ptr(const shared_ptr<type> &t) {}
+ shared_ptr(shared_ptr<type> &&t) {}
+ ~shared_ptr();
+ type &operator*() { return *ptr; }
+ type *operator->() { return ptr; }
+ type *release();
+ void reset();
+ void reset(type *pt);
+ shared_ptr &operator=(shared_ptr &&);
+ template <typename T>
+ shared_ptr &operator=(shared_ptr<T> &&);
+
+private:
+ type *ptr;
+};
+
+} // namespace std
--- /dev/null
+namespace std {
+
+template <typename T>
+class default_delete {};
+
+template <typename type, typename Deleter = std::default_delete<type>>
+class unique_ptr {
+public:
+ unique_ptr();
+ unique_ptr(type *ptr);
+ unique_ptr(const unique_ptr<type> &t) = delete;
+ unique_ptr(unique_ptr<type> &&t);
+ ~unique_ptr();
+ type &operator*() { return *ptr; }
+ type *operator->() { return ptr; }
+ type *release();
+ void reset();
+ void reset(type *pt);
+ void reset(type pt);
+ unique_ptr &operator=(unique_ptr &&);
+ template <typename T>
+ unique_ptr &operator=(unique_ptr<T> &&);
+
+private:
+ type *ptr;
+};
+
+} // namespace std
--- /dev/null
+#ifndef CONTAINERS_H
+#define CONTAINERS_H
+
+namespace std {
+
+template <typename T>
+class iterator {
+public:
+ iterator() {}
+ iterator(const iterator<T> &iter) : ptr(iter.ptr) {}
+
+ typedef T value_type;
+ typedef T *pointer;
+ typedef T &reference;
+
+ reference operator*() const { return *ptr; }
+ pointer operator->() const { return ptr; }
+ iterator &operator++() {
+ ++ptr;
+ return *this;
+ }
+ iterator &operator--() {
+ --ptr;
+ return *this;
+ }
+ iterator operator++(int) {
+ iterator res(*this);
+ ++ptr;
+ return res;
+ }
+ iterator operator--(int) {
+ iterator res(*this);
+ --ptr;
+ return res;
+ }
+ bool operator!=(const iterator<T> &iter) const {
+ return ptr != iter.operator->();
+ }
+
+private:
+ T *ptr;
+};
+
+template <class Iterator>
+class const_iterator {
+public:
+ const_iterator() {}
+ const_iterator(const Iterator &iter) : iter(iter) {}
+ const_iterator(const const_iterator<Iterator> &citer) : iter(citer.iter) {}
+
+ typedef const typename Iterator::value_type value_type;
+ typedef const typename Iterator::pointer pointer;
+ typedef const typename Iterator::reference reference;
+
+ reference operator*() const { return *iter; }
+ pointer operator->() const { return iter.operator->(); }
+
+ const_iterator &operator++() { return ++iter; }
+ const_iterator &operator--() { return --iter; }
+ const_iterator operator++(int) { return iter--; }
+ const_iterator operator--(int) { return iter--; }
+
+ bool operator!=(const Iterator &it) const {
+ return iter->operator->() != it.operator->();
+ }
+ bool operator!=(const const_iterator<Iterator> &it) const {
+ return iter.operator->() != it.operator->();
+ }
+
+private:
+ Iterator iter;
+};
+
+template <class Iterator>
+class forward_iterable {
+public:
+ forward_iterable() {}
+ typedef Iterator iterator;
+ typedef const_iterator<Iterator> const_iterator;
+
+ iterator begin() { return _begin; }
+ iterator end() { return _end; }
+
+ const_iterator begin() const { return _begin; }
+ const_iterator end() const { return _end; }
+
+ const_iterator cbegin() const { return _begin; }
+ const_iterator cend() const { return _end; }
+
+private:
+ iterator _begin, _end;
+};
+
+template <class Iterator>
+class reverse_iterator {
+public:
+ reverse_iterator() {}
+ reverse_iterator(const Iterator &iter) : iter(iter) {}
+ reverse_iterator(const reverse_iterator<Iterator> &rit) : iter(rit.iter) {}
+
+ typedef typename Iterator::value_type value_type;
+ typedef typename Iterator::pointer pointer;
+ typedef typename Iterator::reference reference;
+
+ reference operator*() { return *iter; }
+ pointer operator->() { return iter.operator->(); }
+
+ reverse_iterator &operator++() { return --iter; }
+ reverse_iterator &operator--() { return ++iter; }
+ reverse_iterator operator++(int) { return iter--; }
+ reverse_iterator operator--(int) { return iter++; }
+
+private:
+ Iterator iter;
+};
+
+template <class Iterator>
+class backward_iterable {
+public:
+ backward_iterable() {}
+
+ typedef reverse_iterator<Iterator> reverse_iterator;
+ typedef const_iterator<reverse_iterator> const_reverse_iterator;
+
+ reverse_iterator rbegin() { return _rbegin; }
+ reverse_iterator rend() { return _rend; }
+
+ const_reverse_iterator rbegin() const { return _rbegin; }
+ const_reverse_iterator rend() const { return _rend; }
+
+ const_reverse_iterator crbegin() const { return _rbegin; }
+ const_reverse_iterator crend() const { return _rend; }
+
+private:
+ reverse_iterator _rbegin, _rend;
+};
+
+template <class Iterator>
+class bidirectional_iterable : public forward_iterable<Iterator>,
+ public backward_iterable<Iterator> {};
+
+template <typename A, typename B>
+struct pair {
+ pair(A f, B s) : first(f), second(s) {}
+ A first;
+ B second;
+};
+
+class string {
+public:
+ string() {}
+ string(const char *) {}
+};
+
+template <typename T, int n>
+class array : public backward_iterable<iterator<T>> {
+public:
+ array() {}
+
+ typedef T *iterator;
+ typedef const T *const_iterator;
+
+ iterator begin() { return &v[0]; }
+ iterator end() { return &v[n - 1]; }
+
+ const_iterator begin() const { return &v[0]; }
+ const_iterator end() const { return &v[n - 1]; }
+
+ const_iterator cbegin() const { return &v[0]; }
+ const_iterator cend() const { return &v[n - 1]; }
+
+private:
+ T v[n];
+};
+
+template <typename T>
+class deque : public bidirectional_iterable<iterator<T>> {
+public:
+ deque() {}
+};
+
+template <typename T>
+class list : public bidirectional_iterable<iterator<T>> {
+public:
+ list() {}
+};
+
+template <typename T>
+class forward_list : public forward_iterable<iterator<T>> {
+public:
+ forward_list() {}
+};
+
+template <typename T>
+class vector : public bidirectional_iterable<iterator<T>> {
+public:
+ vector() {}
+};
+
+template <typename T>
+class set : public bidirectional_iterable<iterator<T>> {
+public:
+ set() {}
+};
+
+template <typename T>
+class multiset : public bidirectional_iterable<iterator<T>> {
+public:
+ multiset() {}
+};
+
+template <typename key, typename value>
+class map : public bidirectional_iterable<iterator<pair<key, value>>> {
+public:
+ map() {}
+
+ iterator<pair<key, value>> find(const key &) {}
+ const_iterator<iterator<pair<key, value>>> find(const key &) const {}
+};
+
+template <typename key, typename value>
+class multimap : public bidirectional_iterable<iterator<pair<key, value>>> {
+public:
+ multimap() {}
+};
+
+template <typename T>
+class unordered_set : public forward_iterable<iterator<T>> {
+public:
+ unordered_set() {}
+};
+
+template <typename T>
+class unordered_multiset : public forward_iterable<iterator<T>> {
+public:
+ unordered_multiset() {}
+};
+
+template <typename key, typename value>
+class unordered_map : public forward_iterable<iterator<pair<key, value>>> {
+public:
+ unordered_map() {}
+};
+
+template <typename key, typename value>
+class unordered_multimap : public forward_iterable<iterator<pair<key, value>>> {
+public:
+ unordered_multimap() {}
+};
+
+} // namespace std
+
+#endif // CONTAINERS_H
--- /dev/null
+// 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<class T> 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_SHORT 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_FLOAT_COMPLEX 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
--- /dev/null
+void A1(const int &In, int &Out) {
+ if (In > 123) // NOLINT
+ Out = 123;
+}
+
--- /dev/null
+// run: clang-tidy -checks=-*,llvm-include-order -header-filter=.* %s \
+// run: -- -isystem %S/Inputs/Headers -I %S/Inputs/overlapping | \
+// run: not grep "note: this fix will not be applied because it overlaps with another fix"
+
+#include "b.h"
+#include "a.h"
+
+// The comments above are there to match the offset of the #include with the
+// offset of the #includes in the .cpp file.
--- /dev/null
+// struct ABC is expensive to copy and should be
+// passed as a const referece.
+struct ABC {
+ ABC(const ABC&);
+ int get(int) const;
+};
+
+
+int f1(int n, const ABC& v1, const ABC& v2); // line 9
+
+int f1(int n, ABC v1); // line 11
+
+
+
+int f2( int n, const ABC& v2); // line 15
--- /dev/null
+// struct ABC is expensive to copy and should be
+// passed as a const referece.
+struct ABC {
+ ABC(const ABC&);
+ int get(int) const;
+};
+
+
+int f1(int n, ABC v1, ABC v2); // line 9
+
+int f1(int n, ABC v1); // line 11
+
+
+
+int f2( int n, ABC v2); // line 15
--- /dev/null
+#ifndef SYSTEM_HEADER_H
+#define SYSTEM_HEADER_H
+
+#define SYSTEM_MACRO(m) int m = 0
+
+namespace SYSTEM_NS {
+class structure {
+ int member;
+};
+
+float global;
+
+void function() {
+}
+}
+
+#endif // SYSTEM_HEADER_H
--- /dev/null
+#ifndef USER_HEADER_H
+#define USER_HEADER_H
+
+#define USER_MACRO(m) int m = 0
+
+namespace USER_NS {
+class object {
+ int member;
+};
+
+float global;
+
+void function() {
+}
+}
+
+#endif // USER_HEADER_H
--- /dev/null
+class MyClass {
+public:
+ template <template <typename> class S, typename T>
+ S<T> *func1(T *a) {
+ return new S<T>();
+ }
+ template <typename T, T (*S)()>
+ void func2(T a) {
+ S();
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s android-cloexec-creat %t
+
+typedef int mode_t;
+
+extern "C" int creat(const char *path, mode_t, ...);
+extern "C" int create(const char *path, mode_t, ...);
+
+void f() {
+ creat("filename", 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer open() to creat() because open() allows O_CLOEXEC [android-cloexec-creat]
+ // CHECK-FIXES: open ("filename", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
+ create("filename", 0);
+ // CHECK-MESSAGES-NOT: warning:
+ mode_t mode = 0755;
+ creat("filename", mode);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning:
+ // CHECK-FIXES: open ("filename", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+}
+
+namespace i {
+int creat(const char *path, mode_t, ...);
+void g() {
+ creat("filename", 0);
+ // CHECK-MESSAGES-NOT: warning:
+}
+} // namespace i
+
+class C {
+public:
+ int creat(const char *path, mode_t, ...);
+ void h() {
+ creat("filename", 0);
+ // CHECK-MESSAGES-NOT: warning:
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s android-cloexec-fopen %t
+
+#define FILE_OPEN_RO "r"
+
+typedef int FILE;
+
+extern "C" FILE *fopen(const char *filename, const char *mode, ...);
+extern "C" FILE *open(const char *filename, const char *mode, ...);
+
+void f() {
+ fopen("filename", "r");
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'fopen' mode 'e' to set O_CLOEXEC [android-cloexec-fopen]
+ // CHECK-FIXES: fopen("filename", "re");
+
+ fopen("filename", FILE_OPEN_RO);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'fopen' mode 'e'
+ // CHECK-FIXES: fopen("filename", FILE_OPEN_RO "e");
+
+ fopen("filename", "er");
+ // CHECK-MESSAGES-NOT: warning:
+ fopen("filename", "re");
+ // CHECK-MESSAGES-NOT: warning:
+ fopen("filename", "e");
+ // CHECK-MESSAGES-NOT: warning:
+ open("filename", "e");
+ // CHECK-MESSAGES-NOT: warning:
+
+ char *str = "r";
+ fopen("filename", str);
+ // CHECK-MESSAGES-NOT: warning:
+ str = "re";
+ fopen("filename", str);
+ // CHECK-MESSAGES-NOT: warning:
+ char arr[2] = "r";
+ fopen("filename", arr);
+ // CHECK-MESSAGES-NOT: warning:
+ char arr2[3] = "re";
+ fopen("filename", arr2);
+ // CHECK-MESSAGES-NOT: warning:
+}
+
+namespace i {
+int *fopen(const char *filename, const char *mode, ...);
+void g() {
+ fopen("filename", "e");
+ // CHECK-MESSAGES-NOT: warning:
+}
+} // namespace i
+
+class C {
+public:
+ int *fopen(const char *filename, const char *mode, ...);
+ void h() {
+ fopen("filename", "e");
+ // CHECK-MESSAGES-NOT: warning:
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s android-cloexec-open %t
+
+#define O_RDWR 1
+#define O_EXCL 2
+#define __O_CLOEXEC 3
+#define O_CLOEXEC __O_CLOEXEC
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ int _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1); \
+ })
+
+extern "C" int open(const char *fn, int flags, ...);
+extern "C" int open64(const char *fn, int flags, ...);
+extern "C" int openat(int dirfd, const char *pathname, int flags, ...);
+
+void a() {
+ open("filename", O_RDWR);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'open' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+ // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: 'open' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ open("filename", O_RDWR | O_EXCL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'open' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_EXCL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: 'open' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void b() {
+ open64("filename", O_RDWR);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: 'open64' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+ // CHECK-MESSAGES: :[[@LINE-1]]:47: warning: 'open64' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ open64("filename", O_RDWR | O_EXCL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'open64' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_EXCL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: 'open64' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void c() {
+ openat(0, "filename", O_RDWR);
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 'openat' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+ // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: 'openat' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_CLOEXEC
+ openat(0, "filename", O_RDWR | O_EXCL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: 'openat' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_EXCL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:59: warning: 'openat' should use O_CLOEXEC where
+ // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void f() {
+ open("filename", 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'open' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: 3 | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open("filename", 3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: 'open' should use O_CLOEXEC where
+ // CHECK-FIXES: 3 | O_CLOEXEC
+ open64("filename", 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'open64' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: 3 | O_CLOEXEC
+ TEMP_FAILURE_RETRY(open64("filename", 3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: 'open64' should use O_CLOEXEC where
+ // CHECK-FIXES: 3 | O_CLOEXEC
+ openat(0, "filename", 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'openat' should use O_CLOEXEC where possible [android-cloexec-open]
+ // CHECK-FIXES: 3 | O_CLOEXEC
+ TEMP_FAILURE_RETRY(openat(0, "filename", 3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: 'openat' should use O_CLOEXEC where
+ // CHECK-FIXES: 3 | O_CLOEXEC
+
+ int flag = 3;
+ open("filename", flag);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", flag));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", flag);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", flag));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", flag);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", flag));
+ // CHECK-MESSAGES-NOT: warning:
+}
+
+namespace i {
+int open(const char *pathname, int flags, ...);
+int open64(const char *pathname, int flags, ...);
+int openat(int dirfd, const char *pathname, int flags, ...);
+
+void d() {
+ open("filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+}
+
+} // namespace i
+
+void e() {
+ open("filename", O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ open("filename", O_RDWR | O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ open("filename", O_RDWR | O_CLOEXEC | O_EXCL);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_CLOEXEC | O_EXCL));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", O_RDWR | O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", O_RDWR | O_CLOEXEC | O_EXCL);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_CLOEXEC | O_EXCL));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", O_RDWR | O_CLOEXEC);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_CLOEXEC));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", O_RDWR | O_CLOEXEC | O_EXCL);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_CLOEXEC | O_EXCL));
+ // CHECK-MESSAGES-NOT: warning:
+}
+
+class G {
+public:
+ int open(const char *pathname, int flags, ...);
+ int open64(const char *pathname, int flags, ...);
+ int openat(int dirfd, const char *pathname, int flags, ...);
+
+ void h() {
+ open("filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+ open64("filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+ openat(0, "filename", O_RDWR);
+ // CHECK-MESSAGES-NOT: warning:
+ TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+ // CHECK-MESSAGES-NOT: warning:
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s android-cloexec-socket %t
+
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define __O_CLOEXEC 3
+#define SOCK_CLOEXEC __O_CLOEXEC
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ int _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1); \
+ })
+
+extern "C" int socket(int domain, int type, int protocol);
+
+void a() {
+ socket(0, SOCK_STREAM, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'socket' should use SOCK_CLOEXEC where possible [android-cloexec-socket]
+ // CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0)
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: 'socket'
+ // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0))
+ socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'socket'
+ // CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0)
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: 'socket'
+ // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0))
+}
+
+void f() {
+ socket(0, 3, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'socket'
+ // CHECK-FIXES: socket(0, 3 | SOCK_CLOEXEC, 0)
+ TEMP_FAILURE_RETRY(socket(0, 3, 0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'socket'
+ // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, 3 | SOCK_CLOEXEC, 0))
+
+ int flag = 3;
+ socket(0, flag, 0);
+ TEMP_FAILURE_RETRY(socket(0, flag, 0));
+}
+
+namespace i {
+int socket(int domain, int type, int protocol);
+
+void d() {
+ socket(0, SOCK_STREAM, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+ socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+}
+
+} // namespace i
+
+void e() {
+ socket(0, SOCK_CLOEXEC, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_CLOEXEC, 0));
+ socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0));
+ socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0));
+}
+
+class G {
+public:
+ int socket(int domain, int type, int protocol);
+ void d() {
+ socket(0, SOCK_STREAM, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+ socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+ TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+ }
+};
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- | FileCheck %s
+// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-tidy -Xclang -plugin-arg-clang-tidy -Xclang -checks='-*,llvm-namespace-comment' 2>&1 | FileCheck %s
+
+namespace i {
+}
+// CHECK: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
--- /dev/null
+// RUN: %check_clang_tidy %s boost-use-to-string %t
+
+namespace std {
+
+template <typename T>
+class basic_string {};
+
+using string = basic_string<char>;
+using wstring = basic_string<wchar_t>;
+}
+
+namespace boost {
+template <typename T, typename V>
+T lexical_cast(const V &) {
+ return T();
+};
+}
+
+struct my_weird_type {};
+
+std::string fun(const std::string &) {}
+
+void test_to_string1() {
+
+ auto xa = boost::lexical_cast<std::string>(5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string instead of boost::lexical_cast<std::string> [boost-use-to-string]
+ // CHECK-FIXES: auto xa = std::to_string(5);
+
+ auto z = boost::lexical_cast<std::string>(42LL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::to_string
+ // CHECK-FIXES: auto z = std::to_string(42LL);
+
+ // this should not trigger
+ fun(boost::lexical_cast<std::string>(42.0));
+ auto non = boost::lexical_cast<my_weird_type>(42);
+ boost::lexical_cast<int>("12");
+}
+
+void test_to_string2() {
+ int a;
+ long b;
+ long long c;
+ unsigned d;
+ unsigned long e;
+ unsigned long long f;
+ float g;
+ double h;
+ long double i;
+ bool j;
+
+ fun(boost::lexical_cast<std::string>(a));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(a));
+ fun(boost::lexical_cast<std::string>(b));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(b));
+ fun(boost::lexical_cast<std::string>(c));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(c));
+ fun(boost::lexical_cast<std::string>(d));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(d));
+ fun(boost::lexical_cast<std::string>(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(e));
+ fun(boost::lexical_cast<std::string>(f));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+ // CHECK-FIXES: fun(std::to_string(f));
+
+ // No change for floating numbers.
+ fun(boost::lexical_cast<std::string>(g));
+ fun(boost::lexical_cast<std::string>(h));
+ fun(boost::lexical_cast<std::string>(i));
+ // And bool.
+ fun(boost::lexical_cast<std::string>(j));
+}
+
+std::string fun(const std::wstring &) {}
+
+void test_to_wstring() {
+ int a;
+ long b;
+ long long c;
+ unsigned d;
+ unsigned long e;
+ unsigned long long f;
+ float g;
+ double h;
+ long double i;
+ bool j;
+
+ fun(boost::lexical_cast<std::wstring>(a));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring instead of boost::lexical_cast<std::wstring> [boost-use-to-string]
+ // CHECK-FIXES: fun(std::to_wstring(a));
+ fun(boost::lexical_cast<std::wstring>(b));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+ // CHECK-FIXES: fun(std::to_wstring(b));
+ fun(boost::lexical_cast<std::wstring>(c));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+ // CHECK-FIXES: fun(std::to_wstring(c));
+ fun(boost::lexical_cast<std::wstring>(d));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+ // CHECK-FIXES: fun(std::to_wstring(d));
+ fun(boost::lexical_cast<std::wstring>(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+ // CHECK-FIXES: fun(std::to_wstring(e));
+ fun(boost::lexical_cast<std::wstring>(f));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+ // CHECK-FIXES: fun(std::to_wstring(f));
+
+ // No change for floating numbers
+ fun(boost::lexical_cast<std::wstring>(g));
+ fun(boost::lexical_cast<std::wstring>(h));
+ fun(boost::lexical_cast<std::wstring>(i));
+ // and bool.
+ fun(boost::lexical_cast<std::wstring>(j));
+}
+
+const auto glob = boost::lexical_cast<std::string>(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use std::to_string
+// CHECK-FIXES: const auto glob = std::to_string(42);
+
+template <typename T>
+void string_as_T(T t = T()) {
+ boost::lexical_cast<std::string>(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use std::to_string
+ // CHECK-FIXES: std::to_string(42);
+
+ boost::lexical_cast<T>(42);
+ string_as_T(boost::lexical_cast<T>(42));
+ auto p = boost::lexical_cast<T>(42);
+ auto p2 = (T)boost::lexical_cast<T>(42);
+ auto p3 = static_cast<T>(boost::lexical_cast<T>(42));
+}
+
+#define my_to_string boost::lexical_cast<std::string>
+
+void no_fixup_inside_macro() {
+ my_to_string(12);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use std::to_string
+}
+
+void no_warnings() {
+ fun(boost::lexical_cast<std::string>("abc"));
+ fun(boost::lexical_cast<std::wstring>("abc"));
+ fun(boost::lexical_cast<std::string>(my_weird_type{}));
+ string_as_T<int>();
+ string_as_T<std::string>();
+}
+
+struct Fields {
+ int integer;
+ float floating;
+ Fields* wierd;
+ const int &getConstInteger() const {return integer;}
+};
+
+void testFields() {
+ Fields fields;
+ auto s1 = boost::lexical_cast<std::string>(fields.integer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string
+ // CHECK-FIXES: auto s1 = std::to_string(fields.integer);
+
+ auto s2 = boost::lexical_cast<std::string>(fields.floating);
+ auto s3 = boost::lexical_cast<std::string>(fields.wierd);
+ auto s4 = boost::lexical_cast<std::string>(fields.getConstInteger());
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string
+ // CHECK-FIXES: auto s4 = std::to_string(fields.getConstInteger());
+}
--- /dev/null
+// RUN: %check_clang_tidy %s bugprone-suspicious-memset-usage %t
+
+void *memset(void *, int, __SIZE_TYPE__);
+
+namespace std {
+ using ::memset;
+}
+
+template <typename T>
+void mtempl(int *ptr) {
+ memset(ptr, '0', sizeof(T));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(ptr, 0, sizeof(T));
+ memset(ptr, 256, sizeof(T));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+ memset(0, sizeof(T), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(0, 0, sizeof(T));
+ memset(0, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(0, 0, sizeof(int));
+}
+
+void foo(int xsize, int ysize) {
+ int i[5] = {1, 2, 3, 4, 5};
+ char ca[3] = {'a', 'b', 'c'};
+ int *p = i;
+ int l = 5;
+ char z = '1';
+ char *c = &z;
+ int v = 0;
+
+ memset(p, '0', l);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(p, 0, l);
+
+ memset(p, 0xabcd, l);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+
+ memset(p, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(p, 0, sizeof(int));
+ std::memset(p, sizeof(int), 0x00);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: std::memset(p, 0x00, sizeof(int));
+
+#define M_CHAR_ZERO memset(p, '0', l);
+ M_CHAR_ZERO
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+
+#define M_OUTSIDE_RANGE memset(p, 0xabcd, l);
+ M_OUTSIDE_RANGE
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+
+#define M_ZERO_LENGTH memset(p, sizeof(int), 0);
+ M_ZERO_LENGTH
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+
+ memset(p, '2', l);
+ memset(p, 0, l);
+ memset(c, '0', 1);
+ memset(ca, '0', sizeof(ca));
+
+ memset(p, 0x00, l);
+ mtempl<int>(p);
+
+ memset(p, sizeof(int), v + 1);
+ memset(p, 0xcd, 1);
+
+ // Don't warn when the fill char and the length are both known to be
+ // zero. No bug is possible.
+ memset(p, 0, v);
+
+ // -1 is clearly not a length by virtue of being negative, so no warning
+ // despite v == 0.
+ memset(p, -1, v);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s bugprone-undefined-memory-manipulation %t
+
+void *memset(void *, int, __SIZE_TYPE__);
+void *memcpy(void *, const void *, __SIZE_TYPE__);
+void *memmove(void *, const void *, __SIZE_TYPE__);
+
+namespace std {
+using ::memcpy;
+using ::memmove;
+using ::memset;
+}
+
+// TriviallyCopyable types:
+struct Plain {
+ int n;
+};
+
+enum E {
+ X,
+ Y,
+ Z
+};
+
+struct Base {
+ float b;
+};
+
+struct Derived : Base {
+ bool d;
+};
+
+// not TriviallyCopyable types:
+struct Destruct {
+ ~Destruct() {}
+};
+
+struct Copy {
+ Copy() {}
+ Copy(const Copy &) {}
+};
+
+struct Move {
+ Move() {}
+ Move(Move &&) {}
+};
+
+struct VirtualFunc {
+ virtual void f() {}
+};
+
+struct VirtualBase : virtual Base {
+ int vb;
+};
+
+template <typename T>
+void memset_temp(T *b) {
+ memset(b, 0, sizeof(T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+template <typename S, typename T>
+void memcpy_temp(S *a, T *b) {
+ memcpy(a, b, sizeof(T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+template <typename S, typename T>
+void memmove_temp(S *a, T *b) {
+ memmove(a, b, sizeof(T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+void notTriviallyCopyable() {
+ Plain p; // TriviallyCopyable for variety
+ Destruct d;
+ Copy c;
+ Move m;
+ VirtualFunc vf;
+ VirtualBase vb;
+
+ memset(&vf, 0, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memset(&d, 0, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memset(&c, 0, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ std::memset(&m, 0, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ ::memset(&vb, 0, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+ memcpy(&p, &vf, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memcpy(&p, &d, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memcpy(&c, &p, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ std::memcpy(&m, &p, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ ::memcpy(&vb, &p, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+ memmove(&vf, &p, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memmove(&d, &p, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ memmove(&p, &c, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ std::memmove(&p, &m, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+ ::memmove(&p, &vb, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+#define MEMSET memset(&vf, 0, sizeof(int));
+ MEMSET
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+#define MEMCPY memcpy(&d, &p, sizeof(int));
+ MEMCPY
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+#define MEMMOVE memmove(&p, &c, sizeof(int));
+ MEMMOVE
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+ memset_temp<VirtualFunc>(&vf);
+ memcpy_temp<Plain, VirtualFunc>(&p, &vf);
+ memmove_temp<Plain, VirtualFunc>(&p, &vf);
+}
+
+void triviallyCopyable() {
+ Plain p;
+ Base base;
+ Derived derived;
+
+ int i = 5;
+ int ia[3] = {1, 2, 3};
+ float f = 3.14;
+ float fa[3] = {1.1, 2.2, 3.3};
+ bool b = false;
+ bool ba[2] = {true, false};
+ E e = X;
+ p.n = 2;
+
+ memset(&p, 0, sizeof(int));
+ memset(&base, 0, sizeof(float));
+ memset(&derived, 0, sizeof(bool));
+ memset(&i, 0, sizeof(int));
+ memset(ia, 0, sizeof(int));
+ memset(&f, 0, sizeof(float));
+ memset(fa, 0, sizeof(float));
+ memset(&b, 0, sizeof(bool));
+ memset(ba, 0, sizeof(bool));
+ memset(&e, 0, sizeof(int));
+ memset(&p.n, 0, sizeof(int));
+
+ memcpy(&p, &p, sizeof(int));
+ memcpy(&base, &base, sizeof(float));
+ memcpy(&derived, &derived, sizeof(bool));
+ memcpy(&i, &i, sizeof(int));
+ memcpy(ia, ia, sizeof(int));
+ memcpy(&f, &f, sizeof(float));
+ memcpy(fa, fa, sizeof(float));
+ memcpy(&b, &b, sizeof(bool));
+ memcpy(ba, ba, sizeof(bool));
+ memcpy(&e, &e, sizeof(int));
+ memcpy(&p.n, &p.n, sizeof(int));
+
+ memmove(&p, &p, sizeof(int));
+ memmove(&base, &base, sizeof(float));
+ memmove(&derived, &derived, sizeof(bool));
+ memmove(&i, &i, sizeof(int));
+ memmove(ia, ia, sizeof(int));
+ memmove(&f, &f, sizeof(float));
+ memmove(fa, fa, sizeof(float));
+ memmove(&b, &b, sizeof(bool));
+ memmove(ba, ba, sizeof(bool));
+ memmove(&e, &e, sizeof(int));
+ memmove(&p.n, &p.n, sizeof(int));
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-dcl21-cpp %t
+
+class A {};
+
+A operator++(A &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a non-constant object instead of a constant object type [cert-dcl21-cpp]
+// CHECK-FIXES: {{^}}const A operator++(A &, int);
+
+A operator--(A &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no
+// CHECK-FIXES: {{^}}const A operator--(A &, int);
+
+class B {};
+
+B &operator++(B &);
+const B operator++(B &, int);
+
+B &operator--(B &);
+const B operator--(B &, int);
+
+
+class D {
+D &operator++();
+const D operator++(int);
+
+D &operator--();
+const D operator--(int);
+};
+
+class C {
+C operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a no
+// CHECK-FIXES: {{^}}const C operator++(int);
+
+C operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no
+// CHECK-FIXES: {{^}}const C operator--(int);
+};
+
+class E {};
+
+E &operator++(E &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a reference instead of a constant object type [cert-dcl21-cpp]
+// CHECK-FIXES: {{^}}const E operator++(E &, int);
+
+E &operator--(E &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const E operator--(E &, int);
+
+class G {
+G &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const G operator++(int);
+
+G &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const G operator--(int);
+};
+
+class F {};
+
+const F &operator++(F &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const F operator++(F &, int);
+
+const F &operator--(F &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const F operator--(F &, int);
+
+class H {
+const H &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const H operator++(int);
+
+const H &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const H operator--(int);
+};
+
+
+#define FROM_MACRO P&
+class P {
+const FROM_MACRO operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const FROM_MACRO operator++(int);
+};
+
+
+template<typename T>
+class Q {
+const Q &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const Q<T> operator++(int);
+
+const Q &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const Q<T> operator--(int);
+};
+
+void foobar() {
+ Q<int> a;
+ Q<float> b;
+ (void)a;
+ (void)b;
+}
+
+struct S {};
+typedef S& SRef;
+
+SRef operator++(SRef, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}SRef operator++(SRef, int);
+
+struct T {
+ typedef T& TRef;
+
+ TRef operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}} TRef operator++(int);
+};
+
+struct U {
+ typedef const U& ConstURef;
+
+ ConstURef& operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}} ConstURef& operator++(int);
+};
+
+struct V {
+ V *operator++(int);
+ V *const operator--(int);
+};
+
--- /dev/null
+// RUN: %check_clang_tidy %s cert-dcl58-cpp %t -- -- -std=c++1z -I %S/Inputs/Headers
+
+#include "system-header-simulation.h"
+
+namespace A {
+ namespace B {
+ int b;
+ }
+}
+
+namespace A {
+ namespace B {
+ int c;
+ }
+}
+
+namespace posix {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace can result in undefined behavior [cert-dcl58-cpp]
+ namespace vmi {
+ }
+}
+
+namespace std {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace can
+ int stdInt;
+}
+
+namespace foobar {
+ namespace std {
+ int bar;
+ }
+}
+
+namespace posix::a {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace
+}
+
+enum class MyError {
+ ErrorA,
+ ErrorB
+};
+
+namespace std {
+template <>
+struct is_error_code_enum<MyError> : std::true_type {};
+
+template<>
+void swap<MyError>(MyError &a, MyError &b);
+}
+
+enum class MyError2 {
+ Error2A,
+ Error2B
+};
+
+namespace std {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace
+template <>
+struct is_error_code_enum<MyError2> : std::true_type {};
+
+int foobar;
+}
+
+using namespace std;
+
+int x;
+
--- /dev/null
+// RUN: %check_clang_tidy %s cert-env33-c %t
+
+typedef struct FILE {} FILE;
+
+extern int system(const char *);
+extern FILE *popen(const char *, const char *);
+extern FILE *_popen(const char *, const char *);
+
+void f(void) {
+ // It is permissible to check for the presence of a command processor.
+ system(0);
+
+ system("test");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling 'system' uses a command processor [cert-env33-c]
+
+ popen("test", "test");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling 'popen' uses a command processor
+ _popen("test", "test");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling '_popen' uses a command processor
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-err34-c %t -- -- -std=c11
+
+typedef __SIZE_TYPE__ size_t;
+typedef signed ptrdiff_t;
+typedef long long intmax_t;
+typedef unsigned long long uintmax_t;
+typedef void * FILE;
+
+extern FILE *stdin;
+
+extern int fscanf(FILE * restrict stream, const char * restrict format, ...);
+extern int scanf(const char * restrict format, ...);
+extern int sscanf(const char * restrict s, const char * restrict format, ...);
+
+extern double atof(const char *nptr);
+extern int atoi(const char *nptr);
+extern long int atol(const char *nptr);
+extern long long int atoll(const char *nptr);
+
+void f1(const char *in) {
+ int i;
+ long long ll;
+ unsigned int ui;
+ unsigned long long ull;
+ intmax_t im;
+ uintmax_t uim;
+ float f;
+ double d;
+ long double ld;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ sscanf(in, "%d", &i);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+ fscanf(stdin, "%lld", &ll);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoul' instead [cert-err34-c]
+ sscanf(in, "%u", &ui);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoull' instead [cert-err34-c]
+ fscanf(stdin, "%llu", &ull);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoimax' instead [cert-err34-c]
+ scanf("%jd", &im);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoumax' instead [cert-err34-c]
+ fscanf(stdin, "%ju", &uim);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
+ sscanf(in, "%f", &f); // to float
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+ fscanf(stdin, "%lg", &d);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtold' instead [cert-err34-c]
+ sscanf(in, "%Le", &ld);
+
+ // These are conversions with other modifiers
+ short s;
+ char c;
+ size_t st;
+ ptrdiff_t pt;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%hhd", &c);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%hd", &s);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%zu", &st);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%td", &pt);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%o", ui);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%X", ui);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%x", ui);
+}
+
+void f2(const char *in) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: 'atoi' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ int i = atoi(in); // to int
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: 'atol' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ long l = atol(in); // to long
+ // CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'atoll' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+ long long ll = atoll(in); // to long long
+ // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: 'atof' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+ double d = atof(in); // to double
+}
+
+void f3(void) {
+ int i;
+ unsigned int u;
+ float f;
+ char str[32];
+
+ // Test that we don't report multiple infractions for a single call.
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%d%u%f", &i, &u, &f);
+
+ // Test that we still catch infractions that are not the first specifier.
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+ scanf("%s%d", str, &i);
+}
+
+void do_not_diagnose(void) {
+ char str[32];
+
+ scanf("%s", str); // Not a numerical conversion
+ scanf("%*d"); // Assignment suppressed
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-err34-c %t -- -- -std=c++11
+
+typedef void * FILE;
+
+extern FILE *stdin;
+
+extern int fscanf(FILE * stream, const char * format, ...);
+extern int sscanf(const char * s, const char * format, ...);
+
+extern double atof(const char *nptr);
+extern int atoi(const char *nptr);
+extern long int atol(const char *nptr);
+extern long long int atoll(const char *nptr);
+
+namespace std {
+using ::FILE; using ::stdin;
+using ::fscanf; using ::sscanf;
+using ::atof; using ::atoi; using ::atol; using ::atoll;
+}
+
+void f1(const char *in) {
+ int i;
+ long long ll;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ std::sscanf(in, "%d", &i);
+ // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+ std::fscanf(std::stdin, "%lld", &ll);
+}
+
+void f2(const char *in) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: 'atoi' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ int i = std::atoi(in); // to int
+ // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: 'atol' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+ long l = std::atol(in); // to long
+
+ using namespace std;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'atoll' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+ long long ll = atoll(in); // to long long
+ // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: 'atof' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+ double d = atof(in); // to double
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-flp30-c %t
+
+float g(void);
+
+void func(void) {
+ for (float x = 0.1f; x <= 1.0f; x += 0.1f) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: loop induction expression should not have floating-point type [cert-flp30-c]
+
+ float f = 1.0f;
+ for (; f > 0; --f) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: loop induction expression
+
+ for (;;g()) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: loop induction expression
+
+ for (int i = 0; i < 10; i += 1.0f) {}
+
+ for (int i = 0; i < 10; ++i) {}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-msc30-c %t
+
+extern int rand(void);
+int nonrand();
+
+int cTest() {
+ int i = rand();
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness [cert-msc30-c]
+
+ int k = nonrand();
+
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-msc50-cpp %t
+
+int rand();
+int rand(int);
+
+namespace std {
+using ::rand;
+}
+
+namespace nonstd {
+ int rand();
+}
+
+void testFunction1() {
+ int i = std::rand();
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+
+ int j = ::rand();
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+
+ int k = rand(i);
+
+ int l = nonstd::rand();
+
+ int m = rand();
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+}
+
--- /dev/null
+// RUN: %check_clang_tidy %s cert-oop11-cpp %t -- -- -std=c++11
+
+struct B {
+ B(B&&) noexcept = default;
+
+ B(const B &) = default;
+ B& operator=(const B&) = default;
+ ~B() {}
+};
+
+struct D {
+ B b;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: move constructor initializes class member by calling a copy constructor [cert-oop11-cpp]
+ D(D &&d) : b(d.b) {}
+
+ // This should not produce a diagnostic because it is not covered under
+ // the CERT guideline for OOP11-CPP. However, this will produce a diagnostic
+ // under misc-move-constructor-init.
+ D(B b) : b(b) {}
+};
--- /dev/null
+// RUN: %check_clang_tidy %s cert-err52-cpp %t -- -- -std=c++11
+
+typedef void *jmp_buf;
+extern int __setjmpimpl(jmp_buf);
+#define setjmp(x) __setjmpimpl(x)
+[[noreturn]] extern void longjmp(jmp_buf, int);
+
+namespace std {
+using ::jmp_buf;
+using ::longjmp;
+}
+
+static jmp_buf env;
+void g() {
+ std::longjmp(env, 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead [cert-err52-cpp]
+ ::longjmp(env, 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+ longjmp(env, 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+}
+
+void f() {
+ (void)setjmp(env);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not call 'setjmp'; consider using exception handling instead
+}
--- /dev/null
+// RUN: clang-tidy %s -checks="-*,cert-err58-cpp" -- -std=c++11 -target x86_64-pc-linux-gnu \
+// RUN: | FileCheck %s -check-prefix=CHECK-EXCEPTIONS \
+// RUN: -implicit-check-not="{{warning|error}}:"
+// RUN: clang-tidy %s -checks="-*,cert-err58-cpp" -- -fno-exceptions -std=c++11 -target x86_64-pc-linux-gnu \
+// RUN: | FileCheck %s -allow-empty -check-prefix=CHECK-NONEXCEPTIONS \
+// RUN: -implicit-check-not="{{warning|error}}:"
+
+struct S {
+ S() noexcept(false);
+};
+
+struct T {
+ T() noexcept;
+};
+
+struct U {
+ U() {}
+};
+
+struct V {
+ explicit V(const char *) {} // Can throw
+};
+
+struct Cleanup {
+ ~Cleanup() {}
+};
+
+struct W {
+ W(Cleanup c = {}) noexcept(false);
+};
+
+struct X {
+ X(S = {}) noexcept;
+};
+
+struct Y {
+ S s;
+};
+
+struct Z {
+ T t;
+};
+
+int f();
+int g() noexcept(false);
+int h() noexcept(true);
+
+struct UserConv_Bad {
+ operator int() noexcept(false);
+};
+
+struct UserConv_Good {
+ operator int() noexcept;
+};
+
+UserConv_Bad some_bad_func() noexcept;
+UserConv_Good some_good_func() noexcept;
+
+S s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T t; // ok
+U u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+X x1(S{});
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'x1' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+X x2;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'x2' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+Y y;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'y' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 36:8: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+Z z;
+
+int i = f();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'i' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 44:5: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int j = g();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'j' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 45:5: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int k = h();
+int l = some_bad_func();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'l' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 49:3: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int m = some_good_func();
+
+typedef decltype(sizeof(int)) size_t;
+inline void *operator new(size_t sz, void *here) noexcept { return here; }
+char n[sizeof(int)];
+int *o = new (n) int();
+int *p = new int();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:6: warning: initialization of 'p' with static storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+thread_local S s3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 's3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local T t3; // ok
+thread_local U u3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'u3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local V v3("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'v3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local W w3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'w3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+void f(S s1, T t1, U u1, V v1, W w1) { // ok, ok, ok, ok, ok
+ S s2; // ok
+ T t2; // ok
+ U u2; // ok
+ V v2("v"); // ok
+ W w2; // ok
+
+ thread_local S s3; // ok
+ thread_local T t3; // ok
+ thread_local U u3; // ok
+ thread_local V v3("v"); // ok
+ thread_local W w3; // ok
+
+ static S s4; // ok
+ static T t4; // ok
+ static U u4; // ok
+ static V v4("v"); // ok
+ static W w4; // ok
+}
+
+namespace {
+S s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T t; // ok
+U u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+thread_local S s3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 's3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local T t3; // ok
+thread_local U u3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'u3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local V v3("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'v3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local W w3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'w3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+}; // namespace
+
+class Statics {
+ static S s; // warn when initialized
+ static T t; // ok
+ static U u; // warn when initialized
+ static V v; // warn when initialized
+ static W w; // warn when initialized
+
+ void f(S s, T t, U u, V v) {
+ S s2; // ok
+ T t2; // ok
+ U u2; // ok
+ V v2("v"); // ok
+ W w2; // ok
+
+ thread_local S s3; // ok
+ thread_local T t3; // ok
+ thread_local U u3; // ok
+ thread_local V v3("v"); // ok
+ thread_local W w3; // ok
+
+ static S s4; // ok
+ static T t4; // ok
+ static U u4; // ok
+ static V v4("v"); // ok
+ static W w4; // ok
+ }
+};
+
+S Statics::s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T Statics::t;
+U Statics::u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V Statics::v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W Statics::w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
--- /dev/null
+// RUN: %check_clang_tidy %s cert-err60-cpp %t -- -- -std=c++11 -fcxx-exceptions
+
+struct S {};
+struct T : S {};
+struct U {
+ U() = default;
+ U(const U&) = default;
+};
+
+struct V {
+ V() = default;
+ V(const V&) noexcept;
+};
+
+struct W {
+ W() = default;
+ W(const W&) noexcept(false);
+};
+
+struct X {
+ X() = default;
+ X(const X&) {}
+};
+
+struct Y {
+ Y() = default;
+ Y(const Y&) throw();
+};
+
+struct Z {
+ Z() = default;
+ Z(const Z&) throw(int);
+};
+
+void g() noexcept(false);
+
+struct A {
+ A() = default;
+ A(const A&) noexcept(noexcept(g()));
+};
+
+struct B {
+ B() = default;
+ B(const B&) = default;
+ B(const A&) noexcept(false);
+};
+
+class C {
+ W M; // W is not no-throw copy constructible
+public:
+ C() = default;
+ C(const C&) = default;
+};
+
+struct D {
+ D() = default;
+ D(const D&) noexcept(false);
+ D(D&) noexcept(true);
+};
+
+struct E {
+ E() = default;
+ E(E&) noexcept(true);
+ E(const E&) noexcept(false);
+};
+
+struct Allocates {
+ int *x;
+ Allocates() : x(new int(0)) {}
+ Allocates(const Allocates &other) : x(new int(*other.x)) {}
+};
+
+struct OptionallyAllocates {
+ int *x;
+ OptionallyAllocates() : x(new int(0)) {}
+ OptionallyAllocates(const Allocates &other) noexcept(true) {
+ try {
+ x = new int(*other.x);
+ } catch (...) {
+ x = nullptr;
+ }
+ }
+};
+
+void f() {
+ throw 12; // ok
+ throw "test"; // ok
+ throw S(); // ok
+ throw T(); // ok
+ throw U(); // ok
+ throw V(); // ok
+ throw W(); // match, noexcept(false)
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible [cert-err60-cpp]
+ throw X(); // match, no noexcept clause, nontrivial
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw Y(); // ok
+ throw Z(); // match, throw(int)
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw A(); // match, noexcept(false)
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw B(); // ok
+ throw C(); // match, C has a member variable that makes it throwing on copy
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw D(); // match, has throwing copy constructor
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw E(); // match, has throwing copy constructor
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw Allocates(); // match, copy constructor throws
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+ throw OptionallyAllocates(); // ok
+}
+
+namespace PR25574 {
+struct B {
+ B(const B&) noexcept;
+};
+
+struct D : B {
+ D();
+ virtual ~D() noexcept;
+};
+
+template <typename T>
+void f() {
+ throw D();
+}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cert-dcl50-cpp %t
+
+// Variadic function definitions are diagnosed.
+void f1(int, ...) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: do not define a C-style variadic function; consider using a function parameter pack or currying instead [cert-dcl50-cpp]
+
+// Variadic function *declarations* are not diagnosed.
+void f2(int, ...); // ok
+
+// Function parameter packs are good, however.
+template <typename Arg, typename... Ts>
+void f3(Arg F, Ts... Rest) {}
+
+struct S {
+ void f(int, ...); // ok
+ void f1(int, ...) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not define a C-style variadic function; consider using a function parameter pack or currying instead
+};
+
+// Function definitions that are extern "C" are good.
+extern "C" void f4(int, ...) {} // ok
+extern "C" {
+ void f5(int, ...) {} // ok
+}
--- /dev/null
+#!/usr/bin/env python
+#
+#===- check_clang_tidy.py - ClangTidy Test Helper ------------*- python -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+ClangTidy Test Helper
+=====================
+
+This script runs clang-tidy in fix mode and verify fixes, messages or both.
+
+Usage:
+ check_clang_tidy.py [-resource-dir <resource-dir>] \
+ <source-file> <check-name> <temp-file> \
+ -- [optional clang-tidy arguments]
+
+Example:
+ // RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs
+"""
+
+import argparse
+import re
+import subprocess
+import sys
+
+
+def write_file(file_name, text):
+ with open(file_name, 'w') as f:
+ f.write(text)
+ f.truncate()
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-resource-dir')
+ parser.add_argument('input_file_name')
+ parser.add_argument('check_name')
+ parser.add_argument('temp_file_name')
+
+ args, extra_args = parser.parse_known_args()
+
+ resource_dir = args.resource_dir
+ input_file_name = args.input_file_name
+ check_name = args.check_name
+ temp_file_name = args.temp_file_name
+
+ extension = '.cpp'
+ if (input_file_name.endswith('.c')):
+ extension = '.c'
+ if (input_file_name.endswith('.hpp')):
+ extension = '.hpp'
+ temp_file_name = temp_file_name + extension
+
+ clang_tidy_extra_args = extra_args
+ if len(clang_tidy_extra_args) == 0:
+ clang_tidy_extra_args = ['--', '--std=c++11'] \
+ if extension == '.cpp' or extension == '.hpp' else ['--']
+
+ # Tests should not rely on STL being available, and instead provide mock
+ # implementations of relevant APIs.
+ clang_tidy_extra_args.append('-nostdinc++')
+
+ if resource_dir is not None:
+ clang_tidy_extra_args.append('-resource-dir=%s' % resource_dir)
+
+ with open(input_file_name, 'r') as input_file:
+ input_text = input_file.read()
+
+ has_check_fixes = input_text.find('CHECK-FIXES') >= 0
+ has_check_messages = input_text.find('CHECK-MESSAGES') >= 0
+
+ if not has_check_fixes and not has_check_messages:
+ sys.exit('Neither CHECK-FIXES nor CHECK-MESSAGES found in the input')
+
+ # Remove the contents of the CHECK lines to avoid CHECKs matching on
+ # themselves. We need to keep the comments to preserve line numbers while
+ # avoiding empty lines which could potentially trigger formatting-related
+ # checks.
+ cleaned_test = re.sub('// *CHECK-[A-Z-]*:[^\r\n]*', '//', input_text)
+
+ write_file(temp_file_name, cleaned_test)
+
+ original_file_name = temp_file_name + ".orig"
+ write_file(original_file_name, cleaned_test)
+
+ args = ['clang-tidy', temp_file_name, '-fix', '--checks=-*,' + check_name] + \
+ clang_tidy_extra_args
+ print('Running ' + repr(args) + '...')
+ try:
+ clang_tidy_output = \
+ subprocess.check_output(args, stderr=subprocess.STDOUT).decode()
+ except subprocess.CalledProcessError as e:
+ print('clang-tidy failed:\n' + e.output.decode())
+ raise
+
+ print('------------------------ clang-tidy output -----------------------\n' +
+ clang_tidy_output +
+ '\n------------------------------------------------------------------')
+
+ try:
+ diff_output = subprocess.check_output(
+ ['diff', '-u', original_file_name, temp_file_name],
+ stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ diff_output = e.output
+
+ print('------------------------------ Fixes -----------------------------\n' +
+ diff_output.decode() +
+ '\n------------------------------------------------------------------')
+
+ if has_check_fixes:
+ try:
+ subprocess.check_output(
+ ['FileCheck', '-input-file=' + temp_file_name, input_file_name,
+ '-check-prefix=CHECK-FIXES', '-strict-whitespace'],
+ stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ print('FileCheck failed:\n' + e.output.decode())
+ raise
+
+ if has_check_messages:
+ messages_file = temp_file_name + '.msg'
+ write_file(messages_file, clang_tidy_output)
+ try:
+ subprocess.check_output(
+ ['FileCheck', '-input-file=' + messages_file, input_file_name,
+ '-check-prefix=CHECK-MESSAGES',
+ '-implicit-check-not={{warning|error}}:'],
+ stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ print('FileCheck failed:\n' + e.output.decode())
+ raise
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+// REQUIRES: shell
+// RUN: sed 's/placeholder_for_f/f/' %s > %t.cpp
+// RUN: clang-tidy -checks=-*,modernize-use-override %t.cpp -- -std=c++11 | FileCheck -check-prefix=CHECK-SANITY %s
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -- -std=c++11 2>&1 | FileCheck %s
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -quiet -- -std=c++11 2>&1 | FileCheck -check-prefix=CHECK-QUIET %s
+// RUN: mkdir -p %T/compilation-database-test/
+// RUN: echo '[{"directory": "%T", "command": "clang++ -o test.o -std=c++11 %t.cpp", "file": "%t.cpp"}]' > %T/compilation-database-test/compile_commands.json
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -path %T/compilation-database-test 2>&1 | FileCheck -check-prefix=CHECK %s
+struct A {
+ virtual void f() {}
+ virtual void g() {}
+};
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+struct B : public A {
+ void placeholder_for_f() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK: [[@LINE-2]]:8: warning: annotate this
+// CHECK-QUIET: [[@LINE-3]]:8: warning: annotate this
+ void g() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+};
+// CHECK-SANITY-NOT: Suppressed
+// CHECK: Suppressed 1 warnings (1 due to line filter).
+// CHECK-QUIET-NOT: Suppressed
--- /dev/null
+// RUN: mkdir -p %T/compilation-database-test/include
+// RUN: mkdir -p %T/compilation-database-test/a
+// RUN: mkdir -p %T/compilation-database-test/b
+// RUN: echo 'int *AA = 0;' > %T/compilation-database-test/a/a.cpp
+// RUN: echo 'int *AB = 0;' > %T/compilation-database-test/a/b.cpp
+// RUN: echo 'int *BB = 0;' > %T/compilation-database-test/b/b.cpp
+// RUN: echo 'int *BC = 0;' > %T/compilation-database-test/b/c.cpp
+// RUN: echo 'int *HP = 0;' > %T/compilation-database-test/include/header.h
+// RUN: echo '#include "header.h"' > %T/compilation-database-test/b/d.cpp
+// RUN: sed 's|test_dir|%/T/compilation-database-test|g' %S/Inputs/compilation-database/template.json > %T/compile_commands.json
+// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/b/not-exist -header-filter=.*
+// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp -header-filter=.* -fix
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/a.cpp %s -check-prefix=CHECK-FIX1
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/b.cpp %s -check-prefix=CHECK-FIX2
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/b.cpp %s -check-prefix=CHECK-FIX3
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/c.cpp %s -check-prefix=CHECK-FIX4
+// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX5
+
+// CHECK-FIX1: int *AA = nullptr;
+// CHECK-FIX2: int *AB = nullptr;
+// CHECK-FIX3: int *BB = nullptr;
+// CHECK-FIX4: int *BC = nullptr;
+// CHECK-FIX5: int *HP = nullptr;
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=none --
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=llvm --
+namespace a { class A {}; }
+namespace b {
+using a::A;
+}
+namespace c {}
+// CHECK-MESSAGES: :[[@LINE-3]]:10: warning: using decl 'A' is unused [misc-unused-using-decls]
+// CHECK-FIXES: {{^namespace a { class A {}; }$}}
+// CHECK-FIXES-NOT: namespace
+// CHECK-FIXES: {{^namespace c {}$}}
+// FIXME: cleanupAroundReplacements leaves whitespace. Otherwise we could just
+// check the next line.
--- /dev/null
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-BASE
+// CHECK-BASE: Checks: {{.*}}from-parent
+// CHECK-BASE: HeaderFilterRegex: parent
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/1/- -- | FileCheck %s -check-prefix=CHECK-CHILD1
+// CHECK-CHILD1: Checks: {{.*}}from-child1
+// CHECK-CHILD1: HeaderFilterRegex: child1
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/2/- -- | FileCheck %s -check-prefix=CHECK-CHILD2
+// CHECK-CHILD2: Checks: {{.*}}from-parent
+// CHECK-CHILD2: HeaderFilterRegex: parent
+// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE
+// CHECK-COMMAND-LINE: Checks: {{.*}}from-parent,from-command-line
+// CHECK-COMMAND-LINE: HeaderFilterRegex: from command line
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-interfaces-global-init %t
+
+constexpr int makesInt() { return 3; }
+constexpr int takesInt(int i) { return i + 1; }
+constexpr int takesIntPtr(int *i) { return *i; }
+
+extern int ExternGlobal;
+static int GlobalScopeBadInit1 = ExternGlobal;
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit2 = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit3 = takesIntPtr(&ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit4 = 3 * (ExternGlobal + 2);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+namespace ns {
+static int NamespaceScope = makesInt();
+static int NamespaceScopeBadInit = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+struct A {
+ static int ClassScope;
+ static int ClassScopeBadInit;
+};
+
+int A::ClassScopeBadInit = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+static int FromClassBadInit = takesInt(A::ClassScope);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ClassScope'
+} // namespace ns
+
+// "const int B::I;" is fine, it just ODR-defines B::I. See [9.4.3] Static
+// members [class.static]. However the ODR-definitions are not in the right
+// order (C::I after C::J, see [3.6.2.3]).
+class B1 {
+ static const int I = 0;
+ static const int J = I;
+};
+const int B1::J;
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'I'
+const int B1::I;
+
+void f() {
+ // This is fine, it's executed after dynamic initialization occurs.
+ static int G = takesInt(ExternGlobal);
+}
+
+// Declaration then definition then usage is fine.
+extern int ExternGlobal2;
+extern int ExternGlobal2;
+int ExternGlobal2 = 123;
+static int GlobalScopeGoodInit1 = ExternGlobal2;
+
+
+// Defined global variables are fine:
+static int GlobalScope = makesInt();
+static int GlobalScopeGoodInit2 = takesInt(GlobalScope);
+static int GlobalScope2 = takesInt(ns::NamespaceScope);
+// Enums are fine.
+enum Enum { kEnumValue = 1 };
+static int GlobalScopeFromEnum = takesInt(kEnumValue);
+
+// Leave constexprs alone.
+extern constexpr int GlobalScopeConstexpr = makesInt();
+static constexpr int GlobalScopeConstexprOk =
+ takesInt(GlobalScopeConstexpr);
+
+// This is a pretty common instance: People are usually not using constexpr, but
+// this is what they should write:
+static constexpr const char kValue[] = "value";
+constexpr int Fingerprint(const char *value) { return 0; }
+static int kFingerprint = Fingerprint(kValue);
+
+// This is fine because the ODR-definitions are in the right order (C::J after
+// C::I).
+class B2 {
+ static const int I = 0;
+ static const int J = I;
+};
+const int B2::I;
+const int B2::J;
+
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc;::align_malloc;::calloc"},\
+// RUN: {key: cppcoreguidelines-no-malloc.Reallocations, value: "::realloc;::align_realloc"},\
+// RUN: {key: cppcoreguidelines-no-malloc.Deallocations, value: "::free;::align_free"}]}' \
+// RUN: --
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+void *align_malloc(size_t size, unsigned short aligmnent);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void *align_realloc(void *ptr, size_t size, unsigned short alignment);
+void free(void *ptr);
+void *align_free(void *ptr);
+
+void malloced_array() {
+ int *array0 = (int *)malloc(sizeof(int) * 20);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+ int *zeroed = (int *)calloc(20, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+ int *aligned = (int *)align_malloc(20 * sizeof(int), 16);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+ // reallocation memory, std::vector shall be used
+ char *realloced = (char *)realloc(array0, 50 * sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+ char *align_realloced = (char *)align_realloc(aligned, 50 * sizeof(int), 16);
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+ // freeing memory the bad way
+ free(realloced);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+
+ align_free(align_realloced);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+
+ // check if a call to malloc as function argument is found as well
+ free(malloc(20));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
+
+/// newing an array is still not good, but not relevant to this checker
+void newed_array() {
+ int *new_array = new int[10]; // OK(1)
+}
+
+void arbitrary_call() {
+ // we dont want every function to raise the warning even if malloc is in the name
+ malloced_array(); // OK(2)
+
+ // completly unrelated function call to malloc
+ newed_array(); // OK(3)
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc"},\
+// RUN: {key: cppcoreguidelines-no-malloc.Reallocations, value: ""},\
+// RUN: {key: cppcoreguidelines-no-malloc.Deallocations, value: ""}]}' \
+// RUN: --
+
+// Just ensure, the check will not crash, when no functions shall be checked.
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+
+void malloced_array() {
+ int *array0 = (int *)malloc(sizeof(int) * 20);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+
+void malloced_array() {
+ int *array0 = (int *)malloc(sizeof(int) * 20);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+ int *zeroed = (int *)calloc(20, sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+ // reallocation memory, std::vector shall be used
+ char *realloced = (char *)realloc(array0, 50 * sizeof(int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+ // freeing memory the bad way
+ free(realloced);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+
+ // check if a call to malloc as function argument is found as well
+ free(malloc(20));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
+
+/// newing an array is still not good, but not relevant to this checker
+void newed_array() {
+ int *new_array = new int[10]; // OK(1)
+}
+
+void arbitrary_call() {
+ // we dont want every function to raise the warning even if malloc is in the name
+ malloced_array(); // OK(2)
+
+ // completly unrelated function call to malloc
+ newed_array(); // OK(3)
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-array-to-pointer-decay %t
+#include <stddef.h>
+
+namespace gsl {
+template <class T>
+class array_view {
+public:
+ template <class U, size_t N>
+ array_view(U (&arr)[N]);
+};
+}
+
+void pointerfun(int *p);
+void arrayfun(int p[]);
+void arrayviewfun(gsl::array_view<int> &p);
+size_t s();
+
+void f() {
+ int a[5];
+ pointerfun(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]
+ pointerfun((int *)a); // OK, explicit cast
+ arrayfun(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not implicitly decay an array into a pointer
+
+ pointerfun(a + s() - 10); // Convert to &a[g() - 10];
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not implicitly decay an array into a pointer
+
+ gsl::array_view<int> av(a);
+ arrayviewfun(av); // OK
+
+ int i = a[0]; // OK
+ pointerfun(&a[0]); // OK
+
+ for (auto &e : a) // OK, iteration internally decays array to pointer
+ e = 1;
+}
+
+const char *g() {
+ return "clang"; // OK, decay string literal to pointer
+}
+
+void f2(void *const *);
+void bug25362() {
+ void *a[2];
+ f2(static_cast<void *const*>(a)); // OK, explicit cast
+}
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,cppcoreguidelines-pro-bounds-constant-array-index -- -std=c++03 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+template <int index> struct B {
+ int get() {
+ // The next line used to crash the check (in C++03 mode only).
+ return x[index];
+ }
+ int x[3];
+};
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index %t -- -config='{CheckOptions: [{key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader, value: "dir1/gslheader.h"}]}' -- -std=c++11
+// CHECK-FIXES: #include "dir1/gslheader.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+ template<typename T, size_t N>
+ struct array {
+ T& operator[](size_t n);
+ T& at(size_t n);
+ };
+}
+
+
+namespace gsl {
+ template<class T, size_t N>
+ T& at( T(&a)[N], size_t index );
+
+ template<class T, size_t N>
+ T& at( std::array<T, N> &a, size_t index );
+}
+
+constexpr int const_index(int base) {
+ return base + 3;
+}
+
+void f(std::array<int, 10> a, int pos) {
+ a [ pos / 2 /*comment*/] = 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead [cppcoreguidelines-pro-bounds-constant-array-index]
+ // CHECK-FIXES: gsl::at(a, pos / 2 /*comment*/) = 1;
+ int j = a[pos - 1];
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+ // CHECK-FIXES: int j = gsl::at(a, pos - 1);
+
+ a.at(pos-1) = 2; // OK, at() instead of []
+ gsl::at(a, pos-1) = 2; // OK, gsl::at() instead of []
+
+ a[-1] = 3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index -1 is negative [cppcoreguidelines-pro-bounds-constant-array-index]
+ a[10] = 4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements) [cppcoreguidelines-pro-bounds-constant-array-index]
+
+ a[const_index(7)] = 3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements)
+
+ a[0] = 3; // OK, constant index and inside bounds
+ a[1] = 3; // OK, constant index and inside bounds
+ a[9] = 3; // OK, constant index and inside bounds
+ a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+void g() {
+ int a[10];
+ for (int i = 0; i < 10; ++i) {
+ a[i] = i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+ // CHECK-FIXES: gsl::at(a, i) = i;
+ gsl::at(a, i) = i; // OK, gsl::at() instead of []
+ }
+
+ a[-1] = 3; // flagged by clang-diagnostic-array-bounds
+ a[10] = 4; // flagged by clang-diagnostic-array-bounds
+ a[const_index(7)] = 3; // flagged by clang-diagnostic-array-bounds
+
+ a[0] = 3; // OK, constant index and inside bounds
+ a[1] = 3; // OK, constant index and inside bounds
+ a[9] = 3; // OK, constant index and inside bounds
+ a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+struct S {
+ int& operator[](int i);
+};
+
+void customOperator() {
+ S s;
+ int i = 0;
+ s[i] = 3; // OK, custom operator
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index %t
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+ template<typename T, size_t N>
+ struct array {
+ T& operator[](size_t n);
+ T& at(size_t n);
+ };
+}
+
+
+namespace gsl {
+ template<class T, size_t N>
+ T& at( T(&a)[N], size_t index );
+
+ template<class T, size_t N>
+ T& at( std::array<T, N> &a, size_t index );
+}
+
+constexpr int const_index(int base) {
+ return base + 3;
+}
+
+void f(std::array<int, 10> a, int pos) {
+ a [ pos / 2 /*comment*/] = 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead [cppcoreguidelines-pro-bounds-constant-array-index]
+ int j = a[pos - 1];
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+
+ a.at(pos-1) = 2; // OK, at() instead of []
+ gsl::at(a, pos-1) = 2; // OK, gsl::at() instead of []
+
+ a[-1] = 3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index -1 is negative [cppcoreguidelines-pro-bounds-constant-array-index]
+ a[10] = 4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements) [cppcoreguidelines-pro-bounds-constant-array-index]
+
+ a[const_index(7)] = 3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements)
+
+ a[0] = 3; // OK, constant index and inside bounds
+ a[1] = 3; // OK, constant index and inside bounds
+ a[9] = 3; // OK, constant index and inside bounds
+ a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+void g() {
+ int a[10];
+ for (int i = 0; i < 10; ++i) {
+ a[i] = i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+ // CHECK-FIXES: gsl::at(a, i) = i;
+ gsl::at(a, i) = i; // OK, gsl::at() instead of []
+ }
+
+ a[-1] = 3; // flagged by clang-diagnostic-array-bounds
+ a[10] = 4; // flagged by clang-diagnostic-array-bounds
+ a[const_index(7)] = 3; // flagged by clang-diagnostic-array-bounds
+
+ a[0] = 3; // OK, constant index and inside bounds
+ a[1] = 3; // OK, constant index and inside bounds
+ a[9] = 3; // OK, constant index and inside bounds
+ a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+struct S {
+ int& operator[](int i);
+};
+
+void customOperator() {
+ S s;
+ int i = 0;
+ s[i] = 3; // OK, custom operator
+}
+
+struct A {
+ // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't warn.
+ int x[3];
+};
+
+void use_A() {
+ // Force the compiler to generate a copy constructor.
+ A a;
+ A a2(a);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-pointer-arithmetic %t
+
+enum E {
+ ENUM_LITERAL = 1
+};
+
+int i = 4;
+int j = 1;
+int *p = 0;
+int *q = 0;
+
+void fail() {
+ q = p + 4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
+ p = q + i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+ p = q + ENUM_LITERAL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+
+ q = p - 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+ p = q - i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+ p = q - ENUM_LITERAL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+
+ p += 4;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+ p += i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+ p += ENUM_LITERAL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+
+ q -= 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+ q -= i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+ q -= ENUM_LITERAL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+
+ p++;
+ // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic
+ ++p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+
+ p--;
+ // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic
+ --p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+
+ i = p[1];
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use pointer arithmetic
+}
+
+struct S {
+ operator int() const;
+};
+
+void f(S &s) {
+ int *i;
+ i = i + s;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+}
+
+void f2(int i[]) {
+ i[1] = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+}
+
+void okay() {
+ int a[3];
+ i = a[2]; // OK, access to array
+
+ p = q;
+ p = &i;
+
+ i++;
+ ++i;
+ i--;
+ --i;
+ i += 1;
+ i -= 1;
+ i = j + 1;
+ i = j - 1;
+
+ auto diff = p - q; // OK, result is arithmetic
+
+ for(int ii : a) ; // OK, pointer arithmetic generated by compiler
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-const-cast %t
+
+const int *i;
+int *j;
+void f() { j = const_cast<int *>(i); }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-cstyle-cast %t
+
+void reinterpretcast() {
+ int i = 0;
+ void *j;
+ j = (int*)j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]
+}
+
+void constcast() {
+ int* i;
+ const int* j;
+ i = (int*)j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to cast away constness
+ j = (const int*)i; // OK, const added
+ (void)j; // OK, not a const_cast
+}
+
+void const_and_reinterpret() {
+ int* i;
+ const void* j;
+ i = (int*)j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types
+}
+
+class Base {
+};
+
+class Derived : public Base {
+};
+
+class Base2 {
+};
+
+class MultiDerived : public Base, public Base2 {
+};
+
+class PolymorphicBase {
+public:
+ virtual ~PolymorphicBase();
+};
+
+class PolymorphicDerived : public PolymorphicBase {
+};
+
+class PolymorphicMultiDerived : public Base, public PolymorphicBase {
+};
+
+void pointers() {
+
+ auto P0 = (Derived*)new Base();
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+
+ const Base* B0;
+ auto PC0 = (const Derived*)(B0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class
+
+ auto P1 = (Base*)new Derived(); // OK, upcast to a public base
+ auto P2 = (Base*)new MultiDerived(); // OK, upcast to a public base
+ auto P3 = (Base2*)new MultiDerived(); // OK, upcast to a public base
+}
+
+void pointers_polymorphic() {
+
+ auto PP0 = (PolymorphicDerived*)new PolymorphicBase();
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto PP0 = dynamic_cast<PolymorphicDerived*>(new PolymorphicBase());
+
+ const PolymorphicBase* B0;
+ auto PPC0 = (const PolymorphicDerived*)B0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto PPC0 = dynamic_cast<const PolymorphicDerived*>(B0);
+
+
+ auto B1 = (PolymorphicBase*)new PolymorphicDerived(); // OK, upcast to a public base
+ auto B2 = (PolymorphicBase*)new PolymorphicMultiDerived(); // OK, upcast to a public base
+ auto B3 = (Base*)new PolymorphicMultiDerived(); // OK, upcast to a public base
+}
+
+void arrays() {
+ Base ArrayOfBase[10];
+ auto A0 = (Derived*)ArrayOfBase;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+}
+
+void arrays_polymorphic() {
+ PolymorphicBase ArrayOfPolymorphicBase[10];
+ auto AP0 = (PolymorphicDerived*)ArrayOfPolymorphicBase;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto AP0 = dynamic_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+}
+
+void references() {
+ Base B0;
+ auto R0 = (Derived&)B0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+ Base& RefToBase = B0;
+ auto R1 = (Derived&)RefToBase;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+
+ const Base& ConstRefToBase = B0;
+ auto RC1 = (const Derived&)ConstRefToBase;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class
+
+
+ Derived RD1;
+ auto R2 = (Base&)RD1; // OK, upcast to a public base
+}
+
+void references_polymorphic() {
+ PolymorphicBase B0;
+ auto RP0 = (PolymorphicDerived&)B0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto RP0 = dynamic_cast<PolymorphicDerived&>(B0);
+
+ PolymorphicBase& RefToPolymorphicBase = B0;
+ auto RP1 = (PolymorphicDerived&)RefToPolymorphicBase;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto RP1 = dynamic_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+
+ const PolymorphicBase& ConstRefToPolymorphicBase = B0;
+ auto RPC2 = (const PolymorphicDerived&)(ConstRefToPolymorphicBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto RPC2 = dynamic_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+
+ PolymorphicDerived d1;
+ auto RP2 = (PolymorphicBase&)d1; // OK, upcast to a public base
+}
+
+template<class B, class D>
+void templ() {
+ auto B0 = (B*)new D();
+}
+
+void templ_bad_call() {
+ templ<Derived, Base>(); //FIXME: this should trigger a warning
+}
+
+void templ_good_call() {
+ templ<Base, Derived>(); // OK, upcast to a public base
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -std=c++98 -fno-delayed-template-parsing
+
+struct PositiveFieldBeforeConstructor {
+ int F;
+ PositiveFieldBeforeConstructor() /* some comment */ {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+ // CHECK-FIXES: PositiveFieldBeforeConstructor() : F() /* some comment */ {}
+};
+
+struct PositiveFieldAfterConstructor {
+ PositiveFieldAfterConstructor() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+ // CHECK-FIXES: PositiveFieldAfterConstructor() : F(), G(), H() {}
+ int F;
+ bool G /* with comment */;
+ int *H;
+ PositiveFieldBeforeConstructor IgnoredField;
+};
+
+struct PositiveSeparateDefinition {
+ PositiveSeparateDefinition();
+ int F;
+};
+
+PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+// CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() : F() {}
+
+struct PositiveMixedFieldOrder {
+ PositiveMixedFieldOrder() : /* some comment */ J(0), L(0), M(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K, N
+ // CHECK-FIXES: PositiveMixedFieldOrder() : I(), /* some comment */ J(0), K(), L(0), M(0), N() {}
+ int I;
+ int J;
+ int K;
+ int L;
+ int M;
+ int N;
+};
+
+struct PositiveAfterBaseInitializer : public PositiveMixedFieldOrder {
+ PositiveAfterBaseInitializer() : PositiveMixedFieldOrder() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+ // CHECK-FIXES: PositiveAfterBaseInitializer() : PositiveMixedFieldOrder(), F() {}
+ int F;
+};
+
+struct NegativeFieldInitialized {
+ int F;
+
+ NegativeFieldInitialized() : F() {}
+};
+
+struct NegativeFieldInitializedInDefinition {
+ int F;
+
+ NegativeFieldInitializedInDefinition();
+};
+
+NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
+
+struct NegativeInitializedInBody {
+ NegativeInitializedInBody() { I = 0; }
+ int I;
+};
+
+struct NegativeAggregateType {
+ int X;
+ int Y;
+ int Z;
+};
+
+struct TrivialType {
+ int X;
+ int Y;
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+ public TrivialType {
+ PositiveUninitializedBaseOrdering() : NegativeAggregateType(), TrivialType(), B() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), TrivialType(), A(), B() {}
+
+ // This is somewhat pathological with the base class initializer at the end...
+ PositiveUninitializedBaseOrdering(int) : B(), TrivialType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), TrivialType(), A() {}
+
+ PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: TrivialType
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: B
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), TrivialType(), A(), B() {}
+
+ int A, B;
+};
+
+template <class T>
+class PositiveTemplateBase : T {
+public:
+ PositiveTemplateBase() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X
+ // CHECK-FIXES: PositiveTemplateBase() : X() {}
+
+ int X;
+};
+
+class PositiveIndirectMember {
+ struct {
+ int *A;
+ };
+
+ PositiveIndirectMember() : A() {}
+ PositiveIndirectMember(int) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+ // CHECK-FIXES: PositiveIndirectMember(int) : A() {}
+};
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -fdelayed-template-parsing
+
+template <class T>
+struct PositiveFieldBeforeConstructor {
+ int F;
+ bool G /* with comment */;
+ int *H;
+ PositiveFieldBeforeConstructor() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+};
+// Explicit instantiation.
+template class PositiveFieldBeforeConstructor<int>;
+
+template <class T>
+struct PositiveFieldAfterConstructor {
+ PositiveFieldAfterConstructor() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+ int F;
+ bool G /* with comment */;
+ int *H;
+};
+// Explicit instantiation.
+template class PositiveFieldAfterConstructor<int>;
+
+// This declaration isn't used and won't be parsed 'delayed-template-parsing'.
+// The body of the declaration is 'null' and may cause crash if not handled
+// properly by checkers.
+template <class T>
+struct UnusedDelayedConstructor {
+ UnusedDelayedConstructor() {}
+ int F;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+struct PositiveFieldBeforeConstructor {
+ int F;
+ // CHECK-FIXES: int F{};
+ PositiveFieldBeforeConstructor() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+ // CHECK-FIXES: PositiveFieldBeforeConstructor() {}
+};
+
+struct PositiveFieldAfterConstructor {
+ PositiveFieldAfterConstructor() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G
+ // CHECK-FIXES: PositiveFieldAfterConstructor() {}
+ int F;
+ // CHECK-FIXES: int F{};
+ bool G /* with comment */;
+ // CHECK-FIXES: bool G{} /* with comment */;
+ PositiveFieldBeforeConstructor IgnoredField;
+};
+
+struct PositiveSeparateDefinition {
+ PositiveSeparateDefinition();
+ int F;
+ // CHECK-FIXES: int F{};
+};
+
+PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+// CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+
+struct PositiveMixedFieldOrder {
+ PositiveMixedFieldOrder() : J(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K
+ // CHECK-FIXES: PositiveMixedFieldOrder() : J(0) {}
+ int I;
+ // CHECK-FIXES: int I{};
+ int J;
+ int K;
+ // CHECK-FIXES: int K{};
+};
+
+template <typename T>
+struct Template {
+ Template() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+ int F;
+ // CHECK-FIXES: int F{};
+ T T1;
+ // CHECK-FIXES: T T1;
+};
+
+void instantiate() {
+ Template<int> TInt;
+}
+
+struct NegativeFieldInitialized {
+ int F;
+
+ NegativeFieldInitialized() : F() {}
+};
+
+struct NegativeFieldInitializedInDefinition {
+ int F;
+
+ NegativeFieldInitializedInDefinition();
+};
+NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
+
+struct NegativeInClassInitialized {
+ int F = 0;
+
+ NegativeInClassInitialized() {}
+};
+
+struct NegativeInClassInitializedDefaulted {
+ int F = 0;
+ NegativeInClassInitializedDefaulted() = default;
+};
+
+struct NegativeConstructorDelegated {
+ int F;
+
+ NegativeConstructorDelegated(int F) : F(F) {}
+ NegativeConstructorDelegated() : NegativeConstructorDelegated(0) {}
+};
+
+struct NegativeInitializedInBody {
+ NegativeInitializedInBody() { I = 0; }
+ int I;
+};
+
+struct A {};
+template <class> class AA;
+template <class T> class NegativeTemplateConstructor {
+ NegativeTemplateConstructor(const AA<T> &, A) {}
+ bool Bool{false};
+ // CHECK-FIXES: bool Bool{false};
+};
+
+#define UNINITIALIZED_FIELD_IN_MACRO_BODY(FIELD) \
+ struct UninitializedField##FIELD { \
+ UninitializedField##FIELD() {} \
+ int FIELD; \
+ }; \
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int FIELD;
+
+UNINITIALIZED_FIELD_IN_MACRO_BODY(F);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+UNINITIALIZED_FIELD_IN_MACRO_BODY(G);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
+
+#define UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(ARGUMENT) \
+ ARGUMENT
+
+UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(struct UninitializedFieldInMacroArg {
+ UninitializedFieldInMacroArg() {}
+ int Field;
+});
+// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these fields: Field
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int Field;
+
+struct NegativeAggregateType {
+ int X;
+ int Y;
+ int Z;
+};
+
+struct PositiveTrivialType {
+ PositiveTrivialType() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+
+ NegativeAggregateType F;
+ // CHECK-FIXES: NegativeAggregateType F{};
+};
+
+struct NegativeNonTrivialType {
+ PositiveTrivialType F;
+};
+
+static void PositiveUninitializedTrivialType() {
+ NegativeAggregateType X;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+ // CHECK-FIXES: NegativeAggregateType X{};
+
+ NegativeAggregateType A[10]; // Don't warn because this isn't an object type.
+}
+
+static void NegativeInitializedTrivialType() {
+ NegativeAggregateType X{};
+ NegativeAggregateType Y = {};
+ NegativeAggregateType Z = NegativeAggregateType();
+ NegativeAggregateType A[10]{};
+ NegativeAggregateType B[10] = {};
+ int C; // No need to initialize this because we don't have a constructor.
+ int D[8];
+ NegativeAggregateType E = {0, 1, 2};
+ NegativeAggregateType F({});
+}
+
+struct NonTrivialType {
+ NonTrivialType() = default;
+ NonTrivialType(const NonTrivialType &RHS) : X(RHS.X), Y(RHS.Y) {}
+
+ int X;
+ int Y;
+};
+
+static void PositiveNonTrivialTypeWithCopyConstructor() {
+ NonTrivialType T;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'T'
+ // CHECK-FIXES: NonTrivialType T{};
+
+ NonTrivialType A[8];
+ // Don't warn because this isn't an object type
+}
+
+struct ComplexNonTrivialType {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: Y
+ NegativeFieldInitialized X;
+ int Y;
+ // CHECK-FIXES: int Y{};
+};
+
+static void PositiveComplexNonTrivialType() {
+ ComplexNonTrivialType T;
+}
+
+struct NegativeStaticMember {
+ static NonTrivialType X;
+ static NonTrivialType Y;
+ static constexpr NonTrivialType Z{};
+};
+
+NonTrivialType NegativeStaticMember::X;
+NonTrivialType NegativeStaticMember::Y{};
+
+struct PositiveMultipleConstructors {
+ PositiveMultipleConstructors() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ PositiveMultipleConstructors(int) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ PositiveMultipleConstructors(const PositiveMultipleConstructors &) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+ // FIXME: The fix-its here collide providing an erroneous fix
+ int A, B;
+ // CHECK-FIXES: int A{}{}{}, B{}{}{};
+};
+
+typedef struct {
+ int Member;
+} CStyleStruct;
+
+struct PositiveUninitializedBase : public NegativeAggregateType, CStyleStruct {
+ PositiveUninitializedBase() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, CStyleStruct
+ // CHECK-FIXES: PositiveUninitializedBase() : NegativeAggregateType(), CStyleStruct() {}
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+ public NonTrivialType {
+ PositiveUninitializedBaseOrdering() : B() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, NonTrivialType
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: A
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), B() {}
+
+ // This is somewhat pathological with the base class initializer at the end...
+ PositiveUninitializedBaseOrdering(int) : B(), NonTrivialType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+ PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), A() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NonTrivialType
+ // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+ int A, B;
+ // CHECK-FIXES: int A{}, B;
+};
+
+// We shouldn't need to initialize anything because PositiveUninitializedBase
+// has a user-defined constructor.
+struct NegativeUninitializedBase : public PositiveUninitializedBase {
+ NegativeUninitializedBase() {}
+};
+
+struct InheritedAggregate : public NegativeAggregateType {
+ int F;
+};
+
+static InheritedAggregate NegativeGlobal;
+
+enum TestEnum {
+ A,
+ B,
+ C
+};
+
+enum class TestScopedEnum {
+ A,
+ B,
+ C
+};
+
+struct PositiveEnumType {
+ PositiveEnumType() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X, Y
+ // No proposed fixes, as we don't know whether value initialization for these
+ // enums really makes sense.
+
+ TestEnum X;
+ TestScopedEnum Y;
+};
+
+extern "C" {
+struct NegativeCStruct {
+ int X, Y, Z;
+};
+
+static void PositiveCStructVariable() {
+ NegativeCStruct X;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+ // CHECK-FIXES: NegativeCStruct X{};
+}
+}
+
+static void NegativeStaticVariable() {
+ static NegativeCStruct S;
+ (void)S;
+}
+
+union NegativeUnionInClass {
+ NegativeUnionInClass() {} // No message as a union can only initialize one member.
+ int X = 0;
+ float Y;
+};
+
+union PositiveUnion {
+ PositiveUnion() : X() {} // No message as a union can only initialize one member.
+ PositiveUnion(int) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: union constructor should initialize one of these fields: X, Y
+
+ int X;
+ // CHECK-FIXES: int X{};
+
+ // Make sure we don't give Y an initializer.
+ float Y;
+ // CHECK-FIXES-NOT: float Y{};
+};
+
+struct PositiveAnonymousUnionAndStruct {
+ PositiveAnonymousUnionAndStruct() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B, Y, Z, C, D, E, F, X
+
+ union {
+ int A;
+ // CHECK-FIXES: int A{};
+ short B;
+ };
+
+ struct {
+ int Y;
+ // CHECK-FIXES: int Y{};
+ char *Z;
+ // CHECK-FIXES: char *Z{};
+
+ struct {
+ short C;
+ // CHECK-FIXES: short C{};
+ double D;
+ // CHECK-FIXES: double D{};
+ };
+
+ union {
+ long E;
+ // CHECK-FIXES: long E{};
+ float F;
+ };
+ };
+ int X;
+ // CHECK-FIXES: int X{};
+};
+
+// This check results in a CXXConstructorDecl with no body.
+struct NegativeDeletedConstructor : NegativeAggregateType {
+ NegativeDeletedConstructor() = delete;
+
+ Template<int> F;
+};
+
+// This pathological template fails to compile if actually instantiated. It
+// results in the check seeing a null RecordDecl when examining the base class
+// initializer list.
+template <typename T>
+class PositiveSelfInitialization : NegativeAggregateType
+{
+ PositiveSelfInitialization() : PositiveSelfInitialization() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+ // CHECK-FIXES: PositiveSelfInitialization() : NegativeAggregateType(), PositiveSelfInitialization() {}
+};
+
+class PositiveIndirectMember {
+ struct {
+ int *A;
+ // CHECK-FIXES: int *A{};
+ };
+
+ PositiveIndirectMember() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+};
+
+void Bug30487()
+{
+ NegativeInClassInitializedDefaulted s;
+}
+
+struct PositiveVirtualMethod {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+ int F;
+ // CHECK-FIXES: int F{};
+ virtual int f() = 0;
+};
+
+struct PositiveVirtualDestructor {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+ PositiveVirtualDestructor() = default;
+ int F;
+ // CHECK-FIXES: int F{};
+ virtual ~PositiveVirtualDestructor() {}
+};
+
+struct PositiveVirtualBase : public virtual NegativeAggregateType {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these bases: NegativeAggregateType
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: constructor does not initialize these fields: F
+ int F;
+ // CHECK-FIXES: int F{};
+};
+
+template <typename T>
+struct PositiveTemplateVirtualDestructor {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+ T Val;
+ int F;
+ // CHECK-FIXES: int F{};
+ virtual ~PositiveTemplateVirtualDestructor() = default;
+};
+
+template struct PositiveTemplateVirtualDestructor<int>;
+
+#define UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(FIELD) \
+ struct UninitializedFieldVirtual##FIELD { \
+ int FIELD; \
+ virtual ~UninitializedFieldVirtual##FIELD() {} \
+ }; \
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int FIELD;
+
+UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(F);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(G);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
+
+struct NegativeEmpty {
+};
+
+static void NegativeEmptyVar() {
+ NegativeEmpty e;
+ (void)e;
+}
+
+struct NegativeEmptyMember {
+ NegativeEmptyMember() {}
+ NegativeEmpty e;
+};
+
+struct NegativeEmptyBase : NegativeEmpty {
+ NegativeEmptyBase() {}
+};
+
+struct NegativeEmptyArrayMember {
+ NegativeEmptyArrayMember() {}
+ char e[0];
+};
+
+struct NegativeIncompleteArrayMember {
+ NegativeIncompleteArrayMember() {}
+ char e[];
+};
+
+template <typename T> class NoCrash {
+ class B : public NoCrash {
+ template <typename U> B(U u) {}
+ };
+};
+
+struct PositiveBitfieldMember {
+ PositiveBitfieldMember() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+ unsigned F : 5;
+};
+
+struct NegativeUnnamedBitfieldMember {
+ NegativeUnnamedBitfieldMember() {}
+ unsigned : 5;
+};
+
+struct NegativeInitializedBitfieldMembers {
+ NegativeInitializedBitfieldMembers() : F(3) { G = 2; }
+ unsigned F : 5;
+ unsigned G : 5;
+};
+
+struct NegativeImplicitInheritedCtorBase {
+ NegativeImplicitInheritedCtorBase(unsigned F) : F(F) {}
+ unsigned F;
+};
+
+struct NegativeImplicitInheritedCtor : NegativeImplicitInheritedCtorBase {
+ using NegativeImplicitInheritedCtorBase::NegativeImplicitInheritedCtorBase;
+};
+
+void Bug33557() {
+ NegativeImplicitInheritedCtor I(5);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-reinterpret-cast %t
+
+int i = 0;
+void *j;
+void f() { j = reinterpret_cast<void *>(i); }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-static-cast-downcast %t
+
+class Base {
+};
+
+class Derived : public Base {
+};
+
+class Base2 {
+};
+
+class MultiDerived : public Base, public Base2 {
+};
+
+class PolymorphicBase {
+public:
+ virtual ~PolymorphicBase();
+};
+
+class PolymorphicDerived : public PolymorphicBase {
+};
+
+class PolymorphicMultiDerived : public Base, public PolymorphicBase {
+};
+
+void pointers() {
+
+ auto P0 = static_cast<Derived*>(new Base());
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+ const Base* B0;
+ auto PC0 = static_cast<const Derived*>(B0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+ auto P1 = static_cast<Base*>(new Derived()); // OK, upcast to a public base
+ auto P2 = static_cast<Base*>(new MultiDerived()); // OK, upcast to a public base
+ auto P3 = static_cast<Base2*>(new MultiDerived()); // OK, upcast to a public base
+}
+
+void pointers_polymorphic() {
+
+ auto PP0 = static_cast<PolymorphicDerived*>(new PolymorphicBase());
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+ // CHECK-FIXES: auto PP0 = dynamic_cast<PolymorphicDerived*>(new PolymorphicBase());
+
+ const PolymorphicBase* B0;
+ auto PPC0 = static_cast<const PolymorphicDerived*>(B0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+ // CHECK-FIXES: auto PPC0 = dynamic_cast<const PolymorphicDerived*>(B0);
+
+
+ auto B1 = static_cast<PolymorphicBase*>(new PolymorphicDerived()); // OK, upcast to a public base
+ auto B2 = static_cast<PolymorphicBase*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
+ auto B3 = static_cast<Base*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
+}
+
+void arrays() {
+ Base ArrayOfBase[10];
+ auto A0 = static_cast<Derived*>(ArrayOfBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+}
+
+void arrays_polymorphic() {
+ PolymorphicBase ArrayOfPolymorphicBase[10];
+ auto AP0 = static_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto AP0 = dynamic_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+}
+
+void references() {
+ Base B0;
+ auto R0 = static_cast<Derived&>(B0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+ Base& RefToBase = B0;
+ auto R1 = static_cast<Derived&>(RefToBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+ const Base& ConstRefToBase = B0;
+ auto RC1 = static_cast<const Derived&>(ConstRefToBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+
+ Derived RD1;
+ auto R2 = static_cast<Base&>(RD1); // OK, upcast to a public base
+}
+
+void references_polymorphic() {
+ PolymorphicBase B0;
+ auto RP0 = static_cast<PolymorphicDerived&>(B0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
+ // CHECK-FIXES: auto RP0 = dynamic_cast<PolymorphicDerived&>(B0);
+
+ PolymorphicBase& RefToPolymorphicBase = B0;
+ auto RP1 = static_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+ // CHECK-FIXES: auto RP1 = dynamic_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+
+ const PolymorphicBase& ConstRefToPolymorphicBase = B0;
+ auto RPC2 = static_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+ // CHECK-FIXES: auto RPC2 = dynamic_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+
+ PolymorphicDerived d1;
+ auto RP2 = static_cast<PolymorphicBase&>(d1); // OK, upcast to a public base
+}
+
+template<class B, class D>
+void templ() {
+ auto B0 = static_cast<B*>(new D());
+}
+
+void templ_bad_call() {
+ templ<Derived, Base>(); //FIXME: this should trigger a warning
+}
+
+void templ_good_call() {
+ templ<Base, Derived>(); // OK, upcast to a public base
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-union-access %t
+
+union U {
+ bool union_member1;
+ char union_member2;
+} u;
+
+struct S {
+ int non_union_member;
+ union {
+ bool union_member;
+ };
+ union {
+ char union_member2;
+ } u;
+} s;
+
+
+void f(char);
+void f2(U);
+void f3(U&);
+void f4(U*);
+
+void check()
+{
+ u.union_member1 = true;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not access members of unions; use (boost::)variant instead [cppcoreguidelines-pro-type-union-access]
+ auto b = u.union_member2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not access members of unions; use (boost::)variant instead
+ auto a = &s.union_member;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not access members of unions; use (boost::)variant instead
+ f(s.u.union_member2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not access members of unions; use (boost::)variant instead
+
+ s.non_union_member = 2; // OK
+
+ U u2 = u; // OK
+ f2(u); // OK
+ f3(u); // OK
+ f4(&u); // OK
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-vararg %t
+
+void f(int i);
+void f_vararg(int i, ...);
+
+struct C {
+ void g_vararg(...);
+ void g(const char*);
+} c;
+
+template<typename... P>
+void cpp_vararg(P... p);
+
+void check() {
+ f_vararg(1, 7, 9);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call c-style vararg functions [cppcoreguidelines-pro-type-vararg]
+ c.g_vararg("foo");
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not call c-style vararg functions
+
+ f(3); // OK
+ c.g("foo"); // OK
+ cpp_vararg(1, 7, 9); // OK
+}
+
+// ... as a parameter is allowed (e.g. for SFINAE)
+template <typename T>
+void CallFooIfAvailableImpl(T& t, ...) {
+ // nothing
+}
+template <typename T>
+void CallFooIfAvailableImpl(T& t, decltype(t.foo())*) {
+ t.foo();
+}
+template <typename T>
+void CallFooIfAvailable(T& t) {
+ CallFooIfAvailableImpl(t, 0); // OK to call variadic function when the argument is a literal 0
+}
+
+#include <stdarg.h>
+void my_printf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call c-style vararg functions
+ va_list n;
+ va_copy(n, ap); // Don't warn, va_copy is anyway useless without va_start
+ int i = va_arg(ap, int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use va_start/va_arg to define c-style vararg functions; use variadic templates instead
+ va_end(ap); // Don't warn, va_end is anyway useless without va_start
+}
+
+int my_vprintf(const char* format, va_list arg ); // OK to declare function taking va_list
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-slicing %t
+
+class Base {
+ int i;
+ void f() {}
+ virtual void g() {}
+};
+
+class DerivedWithMemberVariables : public Base {
+ void f();
+ int j;
+};
+
+class TwiceDerivedWithNoMemberVariables : public DerivedWithMemberVariables {
+ void f();
+};
+
+class DerivedWithOverride : public Base {
+ void f();
+ void g() override {}
+};
+
+class TwiceDerivedWithNoOverride : public DerivedWithOverride {
+ void f();
+};
+
+void TakesBaseByValue(Base base);
+
+DerivedWithMemberVariables ReturnsDerived();
+
+void positivesWithMemberVariables() {
+ DerivedWithMemberVariables b;
+ Base a{b};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state [cppcoreguidelines-slicing]
+ a = b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+ TakesBaseByValue(b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+
+ TwiceDerivedWithNoMemberVariables c;
+ a = c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'TwiceDerivedWithNoMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+
+ a = ReturnsDerived();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+}
+
+void positivesWithOverride() {
+ DerivedWithOverride b;
+ Base a{b};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+ a = b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+ TakesBaseByValue(b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+
+ TwiceDerivedWithNoOverride c;
+ a = c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+}
+
+void TakesBaseByReference(Base &base);
+
+class DerivedThatAddsVirtualH : public Base {
+ virtual void h();
+};
+
+class DerivedThatOverridesH : public DerivedThatAddsVirtualH {
+ void h() override;
+};
+
+void negatives() {
+ // OK, simple copying from the same type.
+ Base a;
+ TakesBaseByValue(a);
+ DerivedWithMemberVariables b;
+ DerivedWithMemberVariables c{b};
+ b = c;
+
+ // OK, derived type does not have extra state.
+ TwiceDerivedWithNoMemberVariables d;
+ DerivedWithMemberVariables e{d};
+ e = d;
+
+ // OK, derived does not override any method.
+ TwiceDerivedWithNoOverride f;
+ DerivedWithOverride g{f};
+ g = f;
+
+ // OK, no copying.
+ TakesBaseByReference(d);
+ TakesBaseByReference(f);
+
+ // Derived type overrides methods, but these methods are not in the base type,
+ // so cannot be called accidentally. Right now this triggers, but we might
+ // want to allow it.
+ DerivedThatOverridesH h;
+ a = h;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedThatOverridesH' to 'Base' discards override 'h'
+}
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -- -std=c++03
+
+class DefinesDestructor {
+ ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyConstructor {
+ DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+ DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor or a copy constructor [cppcoreguidelines-special-member-functions]
+
+class DefinesNothing {
+};
+
+class DefinesEverything {
+ DefinesEverything(const DefinesEverything &);
+ DefinesEverything &operator=(const DefinesEverything &);
+ ~DefinesEverything();
+};
+
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: [{key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions, value: 1}, {key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: 1}]}" --
+
+class DefinesDestructor {
+ ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesDefaultedDestructor {
+ ~DefinesDefaultedDestructor() = default;
+};
+
+class DefinesCopyConstructor {
+ DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+ DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor or a copy constructor [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveConstructor {
+ DefinesMoveConstructor(DefinesMoveConstructor &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveAssignment {
+ DefinesMoveAssignment &operator=(DefinesMoveAssignment &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions]
+class DefinesNothing {
+};
+
+class DefinesEverything {
+ DefinesEverything(const DefinesEverything &);
+ DefinesEverything &operator=(const DefinesEverything &);
+ DefinesEverything(DefinesEverything &&);
+ DefinesEverything &operator=(DefinesEverything &&);
+ ~DefinesEverything();
+};
+
+class DeletesEverything {
+ DeletesEverything(const DeletesEverything &) = delete;
+ DeletesEverything &operator=(const DeletesEverything &) = delete;
+ DeletesEverything(DeletesEverything &&) = delete;
+ DeletesEverything &operator=(DeletesEverything &&) = delete;
+ ~DeletesEverything() = delete;
+};
+
+class DeletesCopyDefaultsMove {
+ DeletesCopyDefaultsMove(const DeletesCopyDefaultsMove &) = delete;
+ DeletesCopyDefaultsMove &operator=(const DeletesCopyDefaultsMove &) = delete;
+ DeletesCopyDefaultsMove(DeletesCopyDefaultsMove &&) = default;
+ DeletesCopyDefaultsMove &operator=(DeletesCopyDefaultsMove &&) = default;
+ ~DeletesCopyDefaultsMove() = default;
+};
+
+template <typename T>
+struct TemplateClass {
+ TemplateClass() = default;
+ TemplateClass(const TemplateClass &);
+ TemplateClass &operator=(const TemplateClass &);
+ TemplateClass(TemplateClass &&);
+ TemplateClass &operator=(TemplateClass &&);
+ ~TemplateClass();
+};
+
+// Multiple instantiations of a class template will trigger multiple matches for defined special members.
+// This should not cause problems.
+TemplateClass<int> InstantiationWithInt;
+TemplateClass<double> InstantiationWithDouble;
--- /dev/null
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t
+
+class DefinesDestructor {
+ ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesDefaultedDestructor {
+ ~DefinesDefaultedDestructor() = default;
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDefaultedDestructor' defines a default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyConstructor {
+ DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+ DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor, a copy constructor, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveConstructor {
+ DefinesMoveConstructor(DefinesMoveConstructor &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveAssignment {
+ DefinesMoveAssignment &operator=(DefinesMoveAssignment &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions]
+class DefinesNothing {
+};
+
+class DefinesEverything {
+ DefinesEverything(const DefinesEverything &);
+ DefinesEverything &operator=(const DefinesEverything &);
+ DefinesEverything(DefinesEverything &&);
+ DefinesEverything &operator=(DefinesEverything &&);
+ ~DefinesEverything();
+};
+
+class DeletesEverything {
+ DeletesEverything(const DeletesEverything &) = delete;
+ DeletesEverything &operator=(const DeletesEverything &) = delete;
+ DeletesEverything(DeletesEverything &&) = delete;
+ DeletesEverything &operator=(DeletesEverything &&) = delete;
+ ~DeletesEverything() = delete;
+};
+
+class DeletesCopyDefaultsMove {
+ DeletesCopyDefaultsMove(const DeletesCopyDefaultsMove &) = delete;
+ DeletesCopyDefaultsMove &operator=(const DeletesCopyDefaultsMove &) = delete;
+ DeletesCopyDefaultsMove(DeletesCopyDefaultsMove &&) = default;
+ DeletesCopyDefaultsMove &operator=(DeletesCopyDefaultsMove &&) = default;
+ ~DeletesCopyDefaultsMove() = default;
+};
+
+template <typename T>
+struct TemplateClass {
+ TemplateClass() = default;
+ TemplateClass(const TemplateClass &);
+ TemplateClass &operator=(const TemplateClass &);
+ TemplateClass(TemplateClass &&);
+ TemplateClass &operator=(TemplateClass &&);
+ ~TemplateClass();
+};
+
+// Multiple instantiations of a class template will trigger multiple matches for defined special members.
+// This should not cause problems.
+TemplateClass<int> InstantiationWithInt;
+TemplateClass<double> InstantiationWithDouble;
--- /dev/null
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' %s -- | count 0
+//
+// Enable warnings using -config:
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN: -config='{ExtraArgs: ["-Wshadow","-Wno-unused-variable"], ExtraArgsBefore: ["-Wno-shadow","-Wfloat-conversion","-Wunused-variable"]}' %s -- \
+// RUN: | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+//
+// ... -extra-arg:
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN: -extra-arg=-Wshadow -extra-arg=-Wno-unused-variable \
+// RUN: -extra-arg-before=-Wno-shadow -extra-arg-before=-Wfloat-conversion \
+// RUN: -extra-arg-before=-Wunused-variable %s -- \
+// RUN: | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+//
+// ... a combination of -config and -extra-arg(-before):
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN: -config='{ExtraArgs: ["-Wno-unused-variable"], ExtraArgsBefore: ["-Wno-shadow","-Wfloat-conversion"]}' \
+// RUN: -extra-arg=-Wshadow -extra-arg-before=-Wunused-variable %s -- \
+// RUN: | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+void f(float x) {
+ int a;
+ { int a; }
+ // CHECK: :[[@LINE-1]]:9: warning: declaration shadows a local variable [clang-diagnostic-shadow]
+ int b = x;
+ // CHECK: :[[@LINE-1]]:11: warning: implicit conversion turns floating-point number into integer: 'float' to 'int' [clang-diagnostic-float-conversion]
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-explicit-constructor %t
+
+template<typename T>
+struct A { A(T); };
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: single-argument constructors must be marked explicit
+
+void f() {
+ A<int> a(0);
+ A<double> b(0);
+}
--- /dev/null
+// RUN: clang-tidy -checks='-*,modernize-use-override' %s.nonexistent.cpp -- | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %s -- -fan-unknown-option | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %s -- -fan-unknown-option | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+//
+// Now repeat the tests and ensure no other errors appear on stderr:
+// RUN: clang-tidy -checks='-*,modernize-use-override' %s.nonexistent.cpp -- 2>&1 | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %s -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %s -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE 2>&1 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+//
+// Now create a directory with a compilation database file and ensure we don't
+// use it after failing to parse commands from the command line:
+//
+// RUN: mkdir -p %T/diagnostics/
+// RUN: echo '[{"directory": "%/T/diagnostics/","command": "clang++ -fan-option-from-compilation-database -c %/T/diagnostics/input.cpp", "file": "%/T/diagnostics/input.cpp"}]' > %T/diagnostics/compile_commands.json
+// RUN: cat %s > %T/diagnostics/input.cpp
+// RUN: clang-tidy -checks='-*,modernize-use-override' %T/diagnostics/nonexistent.cpp -- 2>&1 | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %T/diagnostics/input.cpp -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %T/diagnostics/input.cpp -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %T/diagnostics/input.cpp -- -DMACRO_FROM_COMMAND_LINE 2>&1 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %T/diagnostics/input.cpp 2>&1 | FileCheck -check-prefix=CHECK5 -implicit-check-not='{{warning:|error:}}' %s
+
+// CHECK1: error: error reading '{{.*}}nonexistent.cpp' [clang-diagnostic-error]
+// CHECK2: error: unknown argument: '-fan-unknown-option' [clang-diagnostic-error]
+// CHECK3: error: unknown argument: '-fan-unknown-option' [clang-diagnostic-error]
+// CHECK5: error: unknown argument: '-fan-option-from-compilation-database' [clang-diagnostic-error]
+
+// CHECK2: :[[@LINE+3]]:9: warning: implicit conversion from 'double' to 'int' changes value from 1.5 to 1 [clang-diagnostic-literal-conversion]
+// CHECK3: :[[@LINE+2]]:9: warning: implicit conversion from 'double' to 'int' changes value
+// CHECK5: :[[@LINE+1]]:9: warning: implicit conversion from 'double' to 'int' changes value
+int a = 1.5;
+
+// CHECK2: :[[@LINE+3]]:11: warning: single-argument constructors must be marked explicit
+// CHECK3: :[[@LINE+2]]:11: warning: single-argument constructors must be marked explicit
+// CHECK5: :[[@LINE+1]]:11: warning: single-argument constructors must be marked explicit
+class A { A(int) {} };
+
+#define MACRO_FROM_COMMAND_LINE
+// CHECK4: :[[@LINE-1]]:9: warning: 'MACRO_FROM_COMMAND_LINE' macro redefined
--- /dev/null
+// RUN: clang-tidy -checks=-*,modernize-use-nullptr -explain-config | FileCheck --check-prefix=CHECK-MESSAGE1 %s
+// RUN: clang-tidy -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE2 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE3 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,-modernize-use-nullptr'}" %S/Inputs/explain-config/a.cc -explain-config -- | FileCheck --check-prefix=CHECK-MESSAGE4 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-*'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE5 %s
+// RUN: clang-tidy -explain-config %S/Inputs/explain-config/a.cc -- | grep "'modernize-use-nullptr' is enabled in the .*[/\\]Inputs[/\\]explain-config[/\\].clang-tidy."
+
+// CHECK-MESSAGE1: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE2: 'modernize-use-nullptr' is enabled in the command-line option '-config'.
+// CHECK-MESSAGE3: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE4: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE5: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
--- /dev/null
+// RUN: clang-tidy -checks='-*,modernize-use-override' \
+// RUN: -config='{ExtraArgs: ["-DTEST4"], ExtraArgsBefore: ["-DTEST1"]}' \
+// RUN: -extra-arg=-DTEST3 -extra-arg-before=-DTEST2 %s -- -v 2>&1 \
+// RUN: | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// CHECK: {{^}}clang Invocation:{{$}}
+// CHECK-NEXT: {{"-D" "TEST1" .*"-D" "TEST2" .*"-D" "TEST3" .*"-D" "TEST4"}}
--- /dev/null
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK-QUIET %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK2-QUIET %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header2\.h' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header2\.h' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK3-QUIET %s
+// FIXME: "-I %S/Inputs/file-filter/system/.." must be redundant.
+// On Win32, file-filter/system\system-header1.h precedes
+// file-filter\header*.h due to code order between '/' and '\\'.
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers -quiet %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4-QUIET %s
+
+#include "header1.h"
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK2-QUIET: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4: header1.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: header1.h:1:12: warning: single-argument constructors
+
+#include "header2.h"
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2: header2.h:1:12: warning: single-argument constructors
+// CHECK2-QUIET: header2.h:1:12: warning: single-argument constructors
+// CHECK3: header2.h:1:12: warning: single-argument constructors
+// CHECK3-QUIET: header2.h:1:12: warning: single-argument constructors
+// CHECK4: header2.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: header2.h:1:12: warning: single-argument constructors
+
+#include <system-header.h>
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK2-QUIET-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4: system-header.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: system-header.h:1:12: warning: single-argument constructors
+
+class A { A(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors
+// CHECK-QUIET: :[[@LINE-2]]:11: warning: single-argument constructors
+// CHECK2: :[[@LINE-3]]:11: warning: single-argument constructors
+// CHECK2-QUIET: :[[@LINE-4]]:11: warning: single-argument constructors
+// CHECK3: :[[@LINE-5]]:11: warning: single-argument constructors
+// CHECK3-QUIET: :[[@LINE-6]]:11: warning: single-argument constructors
+// CHECK4: :[[@LINE-7]]:11: warning: single-argument constructors
+// CHECK4-QUIET: :[[@LINE-8]]:11: warning: single-argument constructors
+
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK2-QUIET-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4-NOT: warning:
+// CHECK4-QUIET-NOT: warning:
+
+// CHECK: Suppressed 3 warnings (3 in non-user code)
+// CHECK: Use -header-filter=.* to display errors from all non-system headers.
+// CHECK-QUIET-NOT: Suppressed
+// CHECK2: Suppressed 1 warnings (1 in non-user code)
+// CHECK2: Use -header-filter=.* {{.*}}
+// CHECK2-QUIET-NOT: Suppressed
+// CHECK3: Suppressed 2 warnings (2 in non-user code)
+// CHECK3: Use -header-filter=.* {{.*}}
+// CHECK3-QUIET-NOT: Suppressed
+// CHECK4-NOT: Suppressed {{.*}} warnings
+// CHECK4-NOT: Use -header-filter=.* {{.*}}
+// CHECK4-QUIET-NOT: Suppressed
--- /dev/null
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor' -fix -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp -check-prefix=CHECK-FIX %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor' -fix-errors -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp -check-prefix=CHECK-FIX2 %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES2 %s
+
+class A { A(int i); }
+// CHECK-FIX: class A { A(int i); }{{$}}
+// CHECK-MESSAGES: Fixes have NOT been applied.
+// CHECK-FIX2: class A { explicit A(int i); };
+// CHECK-MESSAGES2: note: FIX-IT applied suggested code changes
+// CHECK-MESSAGES2: clang-tidy applied 2 of 2 suggested fixes.
--- /dev/null
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor,llvm-namespace-comment' -fix -export-fixes=%t.yaml -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s
+// RUN: FileCheck -input-file=%t.yaml -check-prefix=CHECK-YAML %s
+
+namespace i {
+void f(); // So that the namespace isn't empty.
+}
+// CHECK: } // namespace i
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-YAML: ReplacementText: ' // namespace i'
+
+class A { A(int i); };
+// CHECK: class A { explicit A(int i); };
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-MESSAGES: clang-tidy applied 2 of 2 suggested fixes.
+// CHECK-YAML: ReplacementText: 'explicit '
--- /dev/null
+// RUN: %check_clang_tidy %s google-build-explicit-make-pair %t
+
+namespace std {
+template <class T1, class T2>
+struct pair {
+ pair(T1 x, T2 y) {}
+};
+
+template <class T1, class T2>
+pair<T1, T2> make_pair(T1 x, T2 y) {
+ return pair<T1, T2>(x, y);
+}
+}
+
+template <typename T>
+void templ(T a, T b) {
+ std::make_pair<T, unsigned>(a, b);
+ std::make_pair<int, int>(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, omit template arguments from make_pair
+// CHECK-FIXES: std::make_pair(1, 2)
+}
+
+template <typename T>
+int t();
+
+void test(int i) {
+ std::make_pair<int, int>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, omit template arguments from make_pair
+// CHECK-FIXES: std::make_pair(i, i)
+
+ std::make_pair<unsigned, int>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, use pair directly
+// CHECK-FIXES: std::pair<unsigned, int>(i, i)
+
+ std::make_pair<int, unsigned>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, use pair directly
+// CHECK-FIXES: std::pair<int, unsigned>(i, i)
+
+#define M std::make_pair<int, unsigned>(i, i);
+M
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: for C++11-compatibility, use pair directly
+// Can't fix in macros.
+// CHECK-FIXES: #define M std::make_pair<int, unsigned>(i, i);
+// CHECK-FIXES-NEXT: M
+
+ templ(i, i);
+ templ(1U, 2U);
+
+ std::make_pair(i, 1); // no-warning
+ std::make_pair(t<int>, 1);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-default-arguments %t
+
+struct A {
+ virtual void f(int I, int J = 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: default arguments on virtual or override methods are prohibited [google-default-arguments]
+};
+
+struct B : public A {
+ void f(int I, int J = 5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: default arguments on virtual or override methods are prohibited
+};
+
+struct C : public B {
+ void f(int I, int J = 5) override;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: default arguments on virtual or override methods are prohibited
+};
+
+// Negatives.
+struct D : public B {
+ void f(int I, int J) override;
+};
+
+struct X {
+ void f(int I, int J = 3);
+};
+
+struct Y : public X {
+ void f(int I, int J = 5);
+};
--- /dev/null
+// RUN: %check_clang_tidy %s google-explicit-constructor %t
+
+namespace std {
+ typedef decltype(sizeof(int)) size_t;
+
+ // libc++'s implementation
+ template <class _E>
+ class initializer_list
+ {
+ const _E* __begin_;
+ size_t __size_;
+
+ initializer_list(const _E* __b, size_t __s)
+ : __begin_(__b),
+ __size_(__s)
+ {}
+
+ public:
+ typedef _E value_type;
+ typedef const _E& reference;
+ typedef const _E& const_reference;
+ typedef size_t size_type;
+
+ typedef const _E* iterator;
+ typedef const _E* const_iterator;
+
+ initializer_list() : __begin_(nullptr), __size_(0) {}
+
+ size_t size() const {return __size_;}
+ const _E* begin() const {return __begin_;}
+ const _E* end() const {return __begin_ + __size_;}
+ };
+}
+
+struct A {
+ A() {}
+ A(int x, int y) {}
+
+ explicit A(void *x) {}
+ explicit A(void *x, void *y) {}
+ explicit operator bool() const { return true; }
+
+ operator double() const = delete;
+
+ explicit A(const A& a) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: copy constructor should not be declared explicit [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}A(const A& a) {}
+
+ A(int x1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}explicit A(int x1);
+
+ A(double x2, double y = 3.14) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructors that are callable with a single argument must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}explicit A(double x2, double y = 3.14) {}
+
+ template <typename... T>
+ A(T&&... args);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructors that are callable with a single argument
+ // CHECK-FIXES: {{^ }}explicit A(T&&... args);
+};
+
+inline A::A(int x1) {}
+
+struct B {
+ B(std::initializer_list<int> list1) {}
+ B(const std::initializer_list<unsigned> &list2) {}
+ B(std::initializer_list<unsigned> &&list3) {}
+
+ operator bool() const { return true; }
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator bool' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}explicit operator bool() const { return true; }
+
+ operator double() const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator double' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}explicit operator double() const;
+
+ explicit B(::std::initializer_list<double> list4) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor should not be declared explicit [google-explicit-constructor]
+ // CHECK-FIXES: {{^ }}B(::std::initializer_list<double> list4) {}
+
+ explicit B(const ::std::initializer_list<char> &list5) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+ // CHECK-FIXES: {{^ }}B(const ::std::initializer_list<char> &list5) {}
+
+ explicit B(::std::initializer_list<char> &&list6) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+ // CHECK-FIXES: {{^ }}B(::std::initializer_list<char> &&list6) {}
+};
+
+inline B::operator double() const { return 0.0; }
+
+struct StructWithFnPointer {
+ void (*f)();
+} struct_with_fn_pointer = {[] {}};
+
+using namespace std;
+
+struct C {
+ C(initializer_list<int> list1) {}
+ C(const initializer_list<unsigned> &list2) {}
+ C(initializer_list<unsigned> &&list3) {}
+};
+
+template <typename T>
+struct C2 {
+ C2(initializer_list<int> list1) {}
+ C2(const initializer_list<unsigned> &list2) {}
+ C2(initializer_list<unsigned> &&list3) {}
+
+ explicit C2(initializer_list<double> list4) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+ // CHECK-FIXES: {{^ }}C2(initializer_list<double> list4) {}
+};
+
+template <typename T>
+struct C3 {
+ C3(initializer_list<T> list1) {}
+ C3(const std::initializer_list<T*> &list2) {}
+ C3(::std::initializer_list<T**> &&list3) {}
+
+ template <typename U>
+ C3(initializer_list<U> list3) {}
+};
+
+struct D {
+ template <typename T>
+ explicit D(T t) {}
+};
+
+template <typename T>
+struct E {
+ E(T *pt) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors
+ // CHECK-FIXES: {{^ }}explicit E(T *pt) {}
+ template <typename U>
+ E(U *pu) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors
+ // CHECK-FIXES: {{^ }}explicit E(U *pu) {}
+
+ explicit E(T t) {}
+ template <typename U>
+ explicit E(U u) {}
+};
+
+void f(std::initializer_list<int> list) {
+ D d(list);
+ E<decltype(list)> e(list);
+ E<int> e2(list);
+}
+
+template <typename T>
+struct F {};
+
+template<typename T>
+struct G {
+ operator bool() const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator bool' must be marked
+ // CHECK-FIXES: {{^}} explicit operator bool() const;
+ operator F<T>() const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator F<type-parameter-0-0>' must be marked
+ // CHECK-FIXES: {{^}} explicit operator F<T>() const;
+ template<typename U>
+ operator F<U>*() const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator F<type-parameter-1-0> *' must be marked
+ // CHECK-FIXES: {{^}} explicit operator F<U>*() const;
+};
+
+void f2() {
+ G<int> a;
+ (void)(F<int>)a;
+ if (a) {}
+ (void)(F<int>*)a;
+ (void)(F<int*>*)a;
+
+ G<double> b;
+ (void)(F<double>)b;
+ if (b) {}
+ (void)(F<double>*)b;
+ (void)(F<double*>*)b;
+}
+
+#define DEFINE_STRUCT_WITH_OPERATOR_BOOL(name) \
+ struct name { \
+ operator bool() const; \
+ }
+
+DEFINE_STRUCT_WITH_OPERATOR_BOOL(H);
--- /dev/null
+// RUN: clang-tidy -checks='-*,google*' -config='{}' -dump-config - -- | FileCheck %s
+// CHECK: CheckOptions:
+// CHECK: {{- key: *google-readability-braces-around-statements.ShortStatementLines}}
+// CHECK-NEXT: {{value: *'1'}}
+// CHECK: {{- key: *google-readability-function-size.StatementThreshold}}
+// CHECK-NEXT: {{value: *'800'}}
+// CHECK: {{- key: *google-readability-namespace-comments.ShortNamespaceLines}}
+// CHECK-NEXT: {{value: *'10'}}
+// CHECK: {{- key: *google-readability-namespace-comments.SpacesBeforeComments}}
+// CHECK-NEXT: {{value: *'2'}}
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,google-build-namespaces,google-build-using-namespace' -header-filter='.*' -- | FileCheck %s -implicit-check-not="{{warning|error}}:"
+#include "Inputs/google-namespaces.h"
+// CHECK: warning: do not use unnamed namespaces in header files [google-build-namespaces]
+
+using namespace spaaaace;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using spaaaace::core; // no-warning
+
+namespace std {
+inline namespace literals {
+inline namespace chrono_literals {
+}
+inline namespace complex_literals {
+}
+inline namespace string_literals {
+}
+}
+}
+
+using namespace std::chrono_literals; // no-warning
+using namespace std::complex_literals; // no-warning
+using namespace std::literals; // no-warning
+using namespace std::literals::chrono_literals; // no-warning
+using namespace std::literals::complex_literals; // no-warning
+using namespace std::literals::string_literals; // no-warning
+using namespace std::string_literals; // no-warning
+
+namespace literals {}
+
+using namespace literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+namespace foo {
+inline namespace literals {
+inline namespace bar_literals {}
+}
+}
+
+using namespace foo::literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using namespace foo::bar_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using namespace foo::literals::bar_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+namespace foo_literals {}
+
+using namespace foo_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
--- /dev/null
+// RUN: %check_clang_tidy %s google-runtime-operator %t
+
+struct Foo {
+ void *operator&();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not overload unary operator&, it is dangerous. [google-runtime-operator]
+};
+
+template <typename T>
+struct TFoo {
+ T *operator&();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not overload unary operator&
+};
+
+TFoo<int> tfoo;
+
+struct Bar;
+void *operator&(Bar &b);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not overload unary operator&
+
+// No warnings on binary operators.
+struct Qux {
+ void *operator&(Qux &q);
+};
+
+void *operator&(Qux &q, Qux &r);
--- /dev/null
+// RUN: %check_clang_tidy %s google-readability-casting %t -- -- -x c
+// The testing script always adds .cpp extension to the input file name, so we
+// need to run clang-tidy directly in order to verify handling of .c files:
+// RUN: clang-tidy --checks=-*,google-readability-casting %s -- -x c++ | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not='{{warning|error}}:'
+// RUN: cp %s %t.main_file.cpp
+// RUN: clang-tidy --checks=-*,google-readability-casting -header-filter='.*' %t.main_file.cpp -- -I%S -DTEST_INCLUDE -x c++ | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not='{{warning|error}}:'
+
+#ifdef TEST_INCLUDE
+
+#undef TEST_INCLUDE
+#include "google-readability-casting.c"
+
+#else
+
+void f(const char *cpc) {
+ const char *cpc2 = (const char*)cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type [google-readability-casting]
+ // CHECK-FIXES: const char *cpc2 = cpc;
+ char *pc = (char*)cpc;
+ typedef const char *Typedef1;
+ (Typedef1)cpc;
+}
+
+#endif
--- /dev/null
+// RUN: %check_clang_tidy %s google-readability-casting %t
+
+bool g() { return false; }
+
+enum Enum { Enum1 };
+struct X {};
+struct Y : public X {};
+
+void f(int a, double b, const char *cpc, const void *cpv, X *pX) {
+ const char *cpc2 = (const char*)cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type [google-readability-casting]
+ // CHECK-FIXES: const char *cpc2 = cpc;
+
+ typedef const char *Typedef1;
+ typedef const char *Typedef2;
+ Typedef1 t1;
+ (Typedef2)t1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: C-style casts are discouraged; use static_cast (if needed, the cast may be redundant) [google-readability-casting]
+ // CHECK-FIXES: {{^}} static_cast<Typedef2>(t1);
+ (const char*)t1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+ // CHECK-FIXES: {{^}} static_cast<const char*>(t1);
+ (Typedef1)cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+ // CHECK-FIXES: {{^}} static_cast<Typedef1>(cpc);
+ (Typedef1)t1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} t1;
+
+ char *pc = (char*)cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use const_cast [google-readability-casting]
+ // CHECK-FIXES: char *pc = const_cast<char*>(cpc);
+ typedef char Char;
+ Char *pChar = (Char*)pc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}}; use static_cast (if needed
+ // CHECK-FIXES: {{^}} Char *pChar = static_cast<Char*>(pc);
+
+ (Char)*cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+ // CHECK-FIXES: {{^}} static_cast<Char>(*cpc);
+
+ (char)*pChar;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+ // CHECK-FIXES: {{^}} static_cast<char>(*pChar);
+
+ (const char*)cpv;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: static_cast<const char*>(cpv);
+
+ char *pc2 = (char*)(cpc + 33);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char *pc2 = const_cast<char*>(cpc + 33);
+
+ const char &crc = *cpc;
+ char &rc = (char&)crc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char &rc = const_cast<char&>(crc);
+
+ char &rc2 = (char&)*cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char &rc2 = const_cast<char&>(*cpc);
+
+ char ** const* const* ppcpcpc;
+ char ****ppppc = (char****)ppcpcpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char ****ppppc = const_cast<char****>(ppcpcpc);
+
+ char ***pppc = (char***)*(ppcpcpc);
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char ***pppc = const_cast<char***>(*(ppcpcpc));
+
+ char ***pppc2 = (char***)(*ppcpcpc);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}}; use const_cast [
+ // CHECK-FIXES: char ***pppc2 = const_cast<char***>(*ppcpcpc);
+
+ char *pc5 = (char*)(const char*)(cpv);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+ // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: char *pc5 = const_cast<char*>(static_cast<const char*>(cpv));
+
+ int b1 = (int)b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: int b1 = static_cast<int>(b);
+ b1 = (const int&)b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+ // CHECK-FIXES: b1 = (const int&)b;
+
+ b1 = (int) b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+ // CHECK-FIXES: b1 = static_cast<int>(b);
+
+ b1 = (int) b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+ // CHECK-FIXES: b1 = static_cast<int>(b);
+
+ b1 = (int) (b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+ // CHECK-FIXES: b1 = static_cast<int>(b);
+
+ b1 = (int) (b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+ // CHECK-FIXES: b1 = static_cast<int>(b);
+
+ Y *pB = (Y*)pX;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+ Y &rB = (Y&)*pX;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+
+ const char *pc3 = (const char*)cpv;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: const char *pc3 = static_cast<const char*>(cpv);
+
+ char *pc4 = (char*)cpv;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+ // CHECK-FIXES: char *pc4 = (char*)cpv;
+
+ b1 = (int)Enum1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: b1 = static_cast<int>(Enum1);
+
+ Enum e = (Enum)b1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [
+ // CHECK-FIXES: Enum e = static_cast<Enum>(b1);
+
+ e = (Enum)Enum1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} e = Enum1;
+
+ e = (Enum)e;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} e = e;
+
+ e = (Enum) e;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} e = e;
+
+ e = (Enum) (e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} e = (e);
+
+ static const int kZero = 0;
+ (int)kZero;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type
+ // CHECK-FIXES: {{^}} kZero;
+
+ int b2 = int(b);
+ int b3 = static_cast<double>(b);
+ int b4 = b;
+ double aa = a;
+ (void)b2;
+ return (void)g();
+}
+
+template <typename T>
+void template_function(T t, int n) {
+ int i = (int)t;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+ // CHECK-FIXES: int i = (int)t;
+ int j = (int)n;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type
+ // CHECK-FIXES: int j = n;
+}
+
+template <typename T>
+struct TemplateStruct {
+ void f(T t, int n) {
+ int k = (int)t;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast
+ // CHECK-FIXES: int k = (int)t;
+ int l = (int)n;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant cast to the same type
+ // CHECK-FIXES: int l = n;
+ }
+};
+
+void test_templates() {
+ template_function(1, 42);
+ template_function(1.0, 42);
+ TemplateStruct<int>().f(1, 42);
+ TemplateStruct<double>().f(1.0, 42);
+}
+
+extern "C" {
+void extern_c_code(const char *cpc) {
+ const char *cpc2 = (const char*)cpc;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type
+ // CHECK-FIXES: const char *cpc2 = cpc;
+ char *pc = (char*)cpc;
+}
+}
+
+#define CAST(type, value) (type)(value)
+void macros(double d) {
+ int i = CAST(int, d);
+}
+
+enum E { E1 = 1 };
+template <E e>
+struct A {
+ // Usage of template argument e = E1 is represented as (E)1 in the AST for
+ // some reason. We have a special treatment of this case to avoid warnings
+ // here.
+ static const E ee = e;
+};
+struct B : public A<E1> {};
+
+
+void overloaded_function();
+void overloaded_function(int);
+
+template<typename Fn>
+void g(Fn fn) {
+ fn();
+}
+
+void function_casts() {
+ typedef void (*FnPtrVoid)();
+ typedef void (&FnRefVoid)();
+ typedef void (&FnRefInt)(int);
+
+ g((void (*)())overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<void (*)()>(overloaded_function));
+ g((void (*)())&overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<void (*)()>(&overloaded_function));
+ g((void (&)())overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<void (&)()>(overloaded_function));
+
+ g((FnPtrVoid)overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<FnPtrVoid>(overloaded_function));
+ g((FnPtrVoid)&overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<FnPtrVoid>(&overloaded_function));
+ g((FnRefVoid)overloaded_function);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: g(static_cast<FnRefVoid>(overloaded_function));
+
+ FnPtrVoid fn0 = (void (*)())&overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: FnPtrVoid fn0 = static_cast<void (*)()>(&overloaded_function);
+ FnPtrVoid fn1 = (void (*)())overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: FnPtrVoid fn1 = static_cast<void (*)()>(overloaded_function);
+ FnPtrVoid fn1a = (FnPtrVoid)overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: FnPtrVoid fn1a = static_cast<FnPtrVoid>(overloaded_function);
+ FnRefInt fn2 = (void (&)(int))overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: FnRefInt fn2 = static_cast<void (&)(int)>(overloaded_function);
+ auto fn3 = (void (*)())&overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: auto fn3 = static_cast<void (*)()>(&overloaded_function);
+ auto fn4 = (void (*)())overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: auto fn4 = static_cast<void (*)()>(overloaded_function);
+ auto fn5 = (void (&)(int))overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: auto fn5 = static_cast<void (&)(int)>(overloaded_function);
+
+ void (*fn6)() = (void (*)())&overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: void (*fn6)() = static_cast<void (*)()>(&overloaded_function);
+ void (*fn7)() = (void (*)())overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: void (*fn7)() = static_cast<void (*)()>(overloaded_function);
+ void (*fn8)() = (FnPtrVoid)overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: void (*fn8)() = static_cast<FnPtrVoid>(overloaded_function);
+ void (&fn9)(int) = (void (&)(int))overloaded_function;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: void (&fn9)(int) = static_cast<void (&)(int)>(overloaded_function);
+
+ void (*correct1)() = static_cast<void (*)()>(overloaded_function);
+ FnPtrVoid correct2 = static_cast<void (*)()>(&overloaded_function);
+ FnRefInt correct3 = static_cast<void (&)(int)>(overloaded_function);
+}
+
+struct S {
+ S(const char *);
+};
+struct ConvertibleToS {
+ operator S() const;
+};
+struct ConvertibleToSRef {
+ operator const S&() const;
+};
+
+void conversions() {
+ //auto s1 = (const S&)"";
+ // C HECK-MESSAGES: :[[@LINE-1]]:10: warning: C-style casts are discouraged; use static_cast [
+ // C HECK-FIXES: S s1 = static_cast<const S&>("");
+ auto s2 = (S)"";
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+ // CHECK-FIXES: auto s2 = S("");
+ auto s2a = (struct S)"";
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+ // CHECK-FIXES: auto s2a = static_cast<struct S>("");
+ auto s2b = (const S)"";
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+ // FIXME: This should be constructor call syntax: S("").
+ // CHECK-FIXES: auto s2b = static_cast<const S>("");
+ ConvertibleToS c;
+ auto s3 = (const S&)c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [
+ // CHECK-FIXES: auto s3 = (const S&)c;
+ // FIXME: This should be a static_cast.
+ // C HECK-FIXES: auto s3 = static_cast<const S&>(c);
+ auto s4 = (S)c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+ // CHECK-FIXES: auto s4 = S(c);
+ ConvertibleToSRef cr;
+ auto s5 = (const S&)cr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [
+ // CHECK-FIXES: auto s5 = (const S&)cr;
+ // FIXME: This should be a static_cast.
+ // C HECK-FIXES: auto s5 = static_cast<const S&>(cr);
+ auto s6 = (S)cr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+ // CHECK-FIXES: auto s6 = S(cr);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-readability-namespace-comments %t
+
+namespace n1 {
+namespace n2 {
+
+
+void f(); // So that the namespace isn't empty.
+
+
+// CHECK-MESSAGES: :[[@LINE+4]]:2: warning: namespace 'n2' not terminated with a closing comment [google-readability-namespace-comments]
+// CHECK-MESSAGES: :[[@LINE-7]]:11: note: namespace 'n2' starts here
+// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: namespace 'n1' not terminated with
+// CHECK-MESSAGES: :[[@LINE-10]]:11: note: namespace 'n1' starts here
+}}
+// CHECK-FIXES: } // namespace n2
+// CHECK-FIXES: } // namespace n1
+
+
+namespace short1 {
+namespace short2 {
+// Namespaces covering 10 lines or fewer are exempt from this rule.
+
+
+
+
+
+}
+}
+
+namespace n3 {
+
+
+
+
+
+
+
+
+
+}; // namespace n3
--- /dev/null
+// RUN: %check_clang_tidy %s google-readability-todo %t -- -config="{User: 'some user'}" --
+
+// TODOfix this1
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this1
+
+// TODO fix this2
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this2
+
+// TODO fix this3
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this3
+
+// TODO: fix this4
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this4
+
+// TODO(clang)fix this5
+
+// TODO(foo):shave yaks
+// TODO(bar):
+// TODO(foo): paint bikeshed
+// TODO(b/12345): find the holy grail
+// TODO (b/12345): allow spaces before parentheses
+// TODO(asdf) allow missing semicolon
--- /dev/null
+// RUN: %check_clang_tidy %s google-runtime-int %t -- \
+// RUN: -config='{CheckOptions: [ \
+// RUN: {key: google-runtime-int.UnsignedTypePrefix, value: "std::uint"}, \
+// RUN: {key: google-runtime-int.SignedTypePrefix, value: "std::int"}, \
+// RUN: {key: google-runtime-int.TypeSuffix, value: "_t"}, \
+// RUN: ]}' -- -std=c++11
+
+long a();
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'std::int{{..}}_t'
+
+typedef unsigned long long uint64; // NOLINT
+
+long b(long = 1);
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'std::int{{..}}_t'
+// CHECK-MESSAGES: [[@LINE-2]]:8: warning: consider replacing 'long' with 'std::int{{..}}_t'
+
+template <typename T>
+void tmpl() {
+ T i;
+}
+
+short bar(const short, unsigned short) {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'short' with 'std::int16_t'
+// CHECK-MESSAGES: [[@LINE-2]]:17: warning: consider replacing 'short' with 'std::int16_t'
+// CHECK-MESSAGES: [[@LINE-3]]:24: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+ long double foo = 42;
+ uint64 qux = 42;
+ unsigned short port;
+
+ const unsigned short bar = 0;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+ long long *baar;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+ const unsigned short &bara = bar;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+ long const long moo = 1;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+ long volatile long wat = 42;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+ unsigned long y;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long' with 'std::uint{{..}}_t'
+ unsigned long long **const *tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'std::uint64_t'
+ unsigned long long **const *&z = tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'std::uint64_t'
+ unsigned short porthole;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+
+ uint64 cast = (short)42;
+// CHECK-MESSAGES: [[@LINE-1]]:18: warning: consider replacing 'short' with 'std::int16_t'
+
+#define l long
+ l x;
+
+ tmpl<short>();
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: consider replacing 'short' with 'std::int16_t'
+}
--- /dev/null
+// RUN: clang-tidy -checks=-*,google-runtime-int %s -- -x c 2>&1 | not grep 'warning:\|error:'
+
+long a();
+
+long b(long x);
+
+short bar(const short q, unsigned short w) {
+ long double foo;
+ unsigned short port;
+
+ const unsigned short bar;
+ long long *baar;
+ const unsigned short bara;
+ long const long moo;
+ long volatile long wat;
+ unsigned long y;
+ unsigned long long **const *tmp;
+ unsigned short porthole;
+
+ unsigned cast;
+ cast = (short)42;
+ return q;
+}
+
+void qux() {
+ short port;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-runtime-int %t
+
+long a();
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'int{{..}}'
+
+typedef unsigned long long uint64; // NOLINT
+
+long b(long = 1);
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'int{{..}}'
+// CHECK-MESSAGES: [[@LINE-2]]:8: warning: consider replacing 'long' with 'int{{..}}'
+
+template <typename T>
+void tmpl() {
+ T i;
+}
+
+short bar(const short, unsigned short) {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'short' with 'int16'
+// CHECK-MESSAGES: [[@LINE-2]]:17: warning: consider replacing 'short' with 'int16'
+// CHECK-MESSAGES: [[@LINE-3]]:24: warning: consider replacing 'unsigned short' with 'uint16'
+ long double foo = 42;
+ uint64 qux = 42;
+ unsigned short port;
+
+ const unsigned short bar = 0;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'uint16'
+ long long *baar;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+ const unsigned short &bara = bar;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'uint16'
+ long const long moo = 1;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+ long volatile long wat = 42;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+ unsigned long y;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long' with 'uint{{..}}'
+ unsigned long long **const *tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'uint64'
+ unsigned long long **const *&z = tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'uint64'
+ unsigned short porthole;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned short' with 'uint16'
+
+ uint64 cast = (short)42;
+// CHECK-MESSAGES: [[@LINE-1]]:18: warning: consider replacing 'short' with 'int16'
+
+#define l long
+ l x;
+
+ tmpl<short>();
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: consider replacing 'short' with 'int16'
+ return 0;
+}
+
+void p(unsigned short port);
+
+void qux() {
+ short port;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'short' with 'int16'
+}
+
+// FIXME: This shouldn't warn, as UD-literal operators require one of a handful
+// of types as an argument.
+struct some_value {};
+constexpr some_value operator"" _some_literal(unsigned long long int i);
+// CHECK-MESSAGES: [[@LINE-1]]:47: warning: consider replacing 'unsigned long long'
+
+struct A { A& operator=(const A&); };
+class B { A a[0]; };
+
+void fff() {
+ B a, b;
+ a = b;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-runtime-member-string-references %t
+
+namespace std {
+template<typename T>
+ class basic_string {};
+
+typedef basic_string<char> string;
+}
+
+class string {};
+
+
+struct A {
+ const std::string &s;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous; it is much better to use alternatives, such as pointers or simple constants [google-runtime-member-string-references]
+};
+
+struct B {
+ std::string &s;
+};
+
+struct C {
+ const std::string s;
+};
+
+template <typename T>
+struct D {
+ D();
+ const T &s;
+ const std::string &s2;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous
+};
+
+D<std::string> d;
+
+struct AA {
+ const string &s;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous
+};
+
+struct BB {
+ string &s;
+};
+
+struct CC {
+ const string s;
+};
+
+D<string> dd;
--- /dev/null
+// RUN: %check_clang_tidy %s google-runtime-references %t -- \
+// RUN: -extra-arg="-std=c++11" \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: google-runtime-references.WhiteListTypes, \
+// RUN: value: 'whitelist::A; whitelist::B'}]}" --
+
+int a;
+int &b = a;
+int *c;
+void f1(int a);
+void f2(int *b);
+void f3(const int &c);
+void f4(int const &d);
+
+// Don't warn on implicit operator= in c++11 mode.
+class A {
+ virtual void f() {}
+};
+// Don't warn on rvalue-references.
+struct A2 {
+ A2(A2&&) = default;
+ void f(A2&&) {}
+};
+
+// Don't warn on iostream parameters.
+namespace xxx {
+class istream { };
+class ostringstream { };
+}
+void g1(xxx::istream &istr);
+void g1(xxx::ostringstream &istr);
+
+void g1(int &a);
+// CHECK-MESSAGES: [[@LINE-1]]:14: warning: non-const reference parameter 'a', make it const or use a pointer [google-runtime-references]
+
+struct s {};
+void g2(int a, int b, s c, s &d);
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: non-const reference parameter 'd', {{.*}}
+
+typedef int &ref;
+void g3(ref a);
+// CHECK-MESSAGES: [[@LINE-1]]:13: warning: non-const reference {{.*}}
+
+void g4(int &a, int &b, int &);
+// CHECK-MESSAGES: [[@LINE-1]]:14: warning: non-const reference parameter 'a', {{.*}}
+// CHECK-MESSAGES: [[@LINE-2]]:22: warning: non-const reference parameter 'b', {{.*}}
+// CHECK-MESSAGES: [[@LINE-3]]:30: warning: non-const reference parameter at index 2, {{.*}}
+
+class B {
+ B(B& a) {}
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: non-const reference {{.*}}
+ virtual void f(int &a) {}
+// CHECK-MESSAGES: [[@LINE-1]]:23: warning: non-const reference {{.*}}
+ void g(int &b);
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: non-const reference {{.*}}
+
+ // Don't warn on the parameter of stream extractors defined as members.
+ B& operator>>(int& val) { return *this; }
+};
+
+// Only warn on the first declaration of each function to reduce duplicate
+// warnings.
+void B::g(int &b) {}
+
+// Don't warn on the first parameter of stream inserters.
+A& operator<<(A& s, int&) { return s; }
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: non-const reference parameter at index 1, {{.*}}
+
+// Don't warn on either parameter of stream extractors. Both need to be
+// non-const references by convention.
+A& operator>>(A& input, int& val) { return input; }
+
+// Don't warn on lambdas.
+auto lambda = [] (int&) {};
+
+// Don't warn on typedefs, as we'll warn on the function itself.
+typedef int (*fp)(int &);
+
+// Don't warn on function references.
+typedef void F();
+void g5(const F& func) {}
+void g6(F& func) {}
+
+template<typename T>
+void g7(const T& t) {}
+
+template<typename T>
+void g8(T t) {}
+
+void f5() {
+ g5(f5);
+ g6(f5);
+ g7(f5);
+ g7<F&>(f5);
+ g8(f5);
+ g8<F&>(f5);
+}
+
+// Don't warn on dependent types.
+template<typename T>
+void g9(T& t) {}
+template<typename T>
+void g10(T t) {}
+
+void f6() {
+ int i;
+ float f;
+ g9<int>(i);
+ g9<const int>(i);
+ g9<int&>(i);
+ g10<int&>(i);
+ g10<float&>(f);
+}
+
+// Warn only on the overridden methods from the base class, as the child class
+// only implements the interface.
+class C : public B {
+ C();
+ virtual void f(int &a) {}
+};
+
+// Don't warn on operator<< with streams-like interface.
+A& operator<<(A& s, int) { return s; }
+
+// Don't warn on swap().
+void swap(C& c1, C& c2) {}
+
+// Don't warn on standalone operator++, operator--, operator+=, operator-=,
+// operator*=, etc. that all need non-const references to be functional.
+A& operator++(A& a) { return a; }
+A operator++(A& a, int) { return a; }
+A& operator--(A& a) { return a; }
+A operator--(A& a, int) { return a; }
+A& operator+=(A& a, const A& b) { return a; }
+A& operator-=(A& a, const A& b) { return a; }
+A& operator*=(A& a, const A& b) { return a; }
+A& operator/=(A& a, const A& b) { return a; }
+A& operator%=(A& a, const A& b) { return a; }
+A& operator<<=(A& a, const A& b) { return a; }
+A& operator>>=(A& a, const A& b) { return a; }
+A& operator|=(A& a, const A& b) { return a; }
+A& operator^=(A& a, const A& b) { return a; }
+A& operator&=(A& a, const A& b) { return a; }
+
+namespace whitelist {
+class A {};
+class B {};
+void f7(A &);
+void f8(B &);
+}
+void f9(whitelist::A &);
+void f10(whitelist::B &);
--- /dev/null
+// RUN: %check_clang_tidy %s hicpp-no-assembler %t
+
+__asm__(".symver foo, bar@v");
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+
+static int s asm("spam");
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+
+void f() {
+ __asm("mov al, 2");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+}
--- /dev/null
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -line-filter='[{"name":"line-filter.cpp","lines":[[18,18],[22,22]]},{"name":"header1.h","lines":[[1,2]]},{"name":"header2.h"},{"name":"header3.h"}]' -header-filter='header[12]\.h$' %s -- -I %S/Inputs/line-filter 2>&1 | FileCheck %s
+
+#include "header1.h"
+// CHECK-NOT: header1.h:{{.*}} warning
+// CHECK: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK: header1.h:2:12: warning: single-argument constructors {{.*}}
+// CHECK-NOT: header1.h:{{.*}} warning
+
+#include "header2.h"
+// CHECK: header2.h:1:12: warning: single-argument constructors {{.*}}
+// CHECK: header2.h:2:12: warning: single-argument constructors {{.*}}
+// CHECK: header2.h:3:12: warning: single-argument constructors {{.*}}
+// CHECK-NOT: header2.h:{{.*}} warning
+
+#include "header3.h"
+// CHECK-NOT: header3.h:{{.*}} warning
+
+class A { A(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors {{.*}}
+class B { B(int); };
+// CHECK-NOT: :[[@LINE-1]]:{{.*}} warning
+class C { C(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors {{.*}}
+
+// CHECK-NOT: warning:
+
+// CHECK: Suppressed 3 warnings (1 in non-user code, 2 due to line filter)
--- /dev/null
+// RUN: mkdir -p %T/clang-tidy/list-checks/
+// RUN: echo '{Checks: "-*,google-*"}' > %T/clang-tidy/.clang-tidy
+// RUN: cd %T/clang-tidy/list-checks
+// RUN: clang-tidy -list-checks | grep "^ *google-"
--- /dev/null
+// RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs/Headers
+
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: #includes are not sorted properly
+#include "j.h"
+#include "gtest/foo.h"
+#include "i.h"
+#include <s.h>
+#include "llvm/a.h"
+#include "clang/b.h"
+#include "clang-c/c.h" // hi
+#include "llvm-c/d.h" // -c
+
+// CHECK-FIXES: #include "j.h"
+// CHECK-FIXES-NEXT: #include "i.h"
+// CHECK-FIXES-NEXT: #include "clang-c/c.h" // hi
+// CHECK-FIXES-NEXT: #include "clang/b.h"
+// CHECK-FIXES-NEXT: #include "llvm-c/d.h" // -c
+// CHECK-FIXES-NEXT: #include "llvm/a.h"
+// CHECK-FIXES-NEXT: #include "gtest/foo.h"
+// CHECK-FIXES-NEXT: #include <s.h>
+
+#include "b.h"
+#ifdef FOO
+#include "a.h"
+#endif
+
+// CHECK-FIXES: #include "b.h"
+// CHECK-FIXES-NEXT: #ifdef FOO
+// CHECK-FIXES-NEXT: #include "a.h"
+// CHECK-FIXES-NEXT: #endif
+
+// CHECK-MESSAGES: [[@LINE+1]]:1: warning: #includes are not sorted properly
+#include "b.h"
+#include "a.h"
+
+// CHECK-FIXES: #include "a.h"
+// CHECK-FIXES-NEXT: #include "b.h"
+
+// CHECK-MESSAGES-NOT: [[@LINE+1]]:1: warning: #includes are not sorted properly
+#include "cross-file-c.h"
+// This line number should correspond to the position of the #include in cross-file-c.h
+#include "cross-file-a.h"
--- /dev/null
+// RUN: %check_clang_tidy %s llvm-twine-local %t
+
+namespace llvm {
+class Twine {
+public:
+ Twine(const char *);
+ Twine(int);
+ Twine();
+ Twine &operator+(const Twine &);
+};
+}
+
+using namespace llvm;
+
+void foo(const Twine &x);
+
+static Twine Moo = Twine("bark") + "bah";
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: static std::string Moo = (Twine("bark") + "bah").str();
+
+int main() {
+ const Twine t = Twine("a") + "b" + Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t = (Twine("a") + "b" + Twine(42)).str();
+ foo(Twine("a") + "b");
+
+ Twine Prefix = false ? "__INT_FAST" : "__UINT_FAST";
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: const char * Prefix = false ? "__INT_FAST" : "__UINT_FAST";
+
+ const Twine t2 = Twine();
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t2 = (Twine()).str();
+ foo(Twine() + "b");
+
+ const Twine t3 = Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t3 = (Twine(42)).str();
+
+ const Twine t4 = Twine(42) + "b";
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t4 = (Twine(42) + "b").str();
+
+ const Twine t5 = Twine() + "b";
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t5 = (Twine() + "b").str();
+
+ const Twine t6 = true ? Twine() : Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t6 = (true ? Twine() : Twine(42)).str();
+
+ const Twine t7 = false ? Twine() : Twine("b");
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t7 = (false ? Twine() : Twine("b")).str();
+}
--- /dev/null
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' %s -- | FileCheck %s
+
+#define Q(name) class name { name(int i); }
+
+Q(A);
+// CHECK: :[[@LINE-1]]:3: warning: single-argument constructors must be marked explicit
+// CHECK: :3:30: note: expanded from macro 'Q'
--- /dev/null
+// RUN: %check_clang_tidy %s misc-argument-comment %t
+
+namespace testing {
+namespace internal {
+
+template <typename F>
+struct Function;
+
+template <typename R>
+struct Function<R()> {
+ typedef R Result;
+};
+
+template <typename R, typename A1>
+struct Function<R(A1)>
+ : Function<R()> {
+ typedef A1 Argument1;
+};
+
+template <typename R, typename A1, typename A2>
+struct Function<R(A1, A2)>
+ : Function<R(A1)> {
+ typedef A2 Argument2;
+};
+
+} // namespace internal
+
+template <typename F>
+class MockSpec {
+ public:
+ void f();
+};
+
+template <typename T>
+class Matcher {
+ public:
+ explicit Matcher();
+ Matcher(T value);
+};
+
+} // namespace testing
+
+#define GMOCK_RESULT_(tn, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::Result
+#define GMOCK_ARG_(tn, N, ...) \
+ tn ::testing::internal::Function<__VA_ARGS__>::Argument##N
+#define GMOCK_MATCHER_(tn, N, ...) \
+ const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&
+#define GMOCK_METHOD2_(tn, constness, ct, Method, ...) \
+ GMOCK_RESULT_(tn, __VA_ARGS__) \
+ ct Method( \
+ GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness; \
+ ::testing::MockSpec<__VA_ARGS__> \
+ gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+ GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness
+#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__)
+#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
+ ((obj).gmock_##call).f()
+#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)
+
+class Base {
+ public:
+ virtual void Method(int param_one_base, int param_two_base);
+};
+class Derived : public Base {
+ public:
+ virtual void Method(int param_one, int param_two);
+ virtual void Method2(int p_one, int p_two) const;
+};
+class MockDerived : public Derived {
+ public:
+ MOCK_METHOD2(Method, void(int a, int b));
+ MOCK_CONST_METHOD2(Method2, void(int c, int d));
+};
+
+class MockStandalone {
+ public:
+ MOCK_METHOD2(Method, void(int aaa, int bbb));
+};
+
+void test_gmock_expectations() {
+ MockDerived m;
+ EXPECT_CALL(m, Method(/*param_one=*/1, /*param_tw=*/2));
+// CHECK-MESSAGES: [[@LINE-1]]:42: warning: argument name 'param_tw' in comment does not match parameter name 'param_two'
+// CHECK-FIXES: EXPECT_CALL(m, Method(/*param_one=*/1, /*param_two=*/2));
+ EXPECT_CALL(m, Method2(/*p_on=*/3, /*p_two=*/4));
+// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on' in comment does not match parameter name 'p_one'
+// CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/3, /*p_two=*/4));
+
+ #define PARAM1 11
+ #define PARAM2 22
+ EXPECT_CALL(m, Method2(/*p_on1=*/PARAM1, /*p_tw2=*/PARAM2));
+// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on1' in comment does not match parameter name 'p_one'
+// CHECK-MESSAGES: [[@LINE-2]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two'
+// CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/PARAM1, /*p_two=*/PARAM2));
+
+ MockStandalone m2;
+ EXPECT_CALL(m2, Method(/*aaa=*/5, /*bbc=*/6));
+}
+
+void test_gmock_direct_calls() {
+ MockDerived m;
+ m.Method(/*param_one=*/1, /*param_tw=*/2);
+// CHECK-MESSAGES: [[@LINE-1]]:29: warning: argument name 'param_tw' in comment does not match parameter name 'param_two'
+// CHECK-FIXES: m.Method(/*param_one=*/1, /*param_two=*/2);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-argument-comment %t -- \
+// RUN: -config="{CheckOptions: [{key: StrictMode, value: 1}]}" --
+
+void f(int _with_underscores_);
+void g(int x_);
+void ignores_underscores() {
+ f(/*With_Underscores=*/0);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'With_Underscores' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/0);
+ f(/*with_underscores=*/1);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'with_underscores' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/1);
+ f(/*_With_Underscores_=*/2);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name '_With_Underscores_' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/2);
+ g(/*X=*/3);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'X' in comment does not match parameter name 'x_'
+// CHECK-FIXES: g(/*x_=*/3);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-argument-comment %t
+
+// FIXME: clang-tidy should provide a -verify mode to make writing these checks
+// easier and more accurate.
+
+void ffff(int xxxx, int yyyy);
+
+void f(int x, int y);
+void g() {
+ // CHECK-MESSAGES: [[@LINE+4]]:5: warning: argument name 'y' in comment does not match parameter name 'x'
+ // CHECK-MESSAGES: :[[@LINE-3]]:12: note: 'x' declared here
+ // CHECK-MESSAGES: [[@LINE+2]]:14: warning: argument name 'z' in comment does not match parameter name 'y'
+ // CHECK-MESSAGES: :[[@LINE-5]]:19: note: 'y' declared here
+ f(/*y=*/0, /*z=*/0);
+ // CHECK-FIXES: {{^}} f(/*y=*/0, /*z=*/0);
+
+ ffff(0 /*aaaa=*/, /*bbbb*/ 0); // Unsupported formats.
+}
+
+struct Closure {};
+
+template <typename T1, typename T2>
+Closure *NewCallback(void (*f)(T1, T2), T1 arg1, T2 arg2) { return nullptr; }
+
+template <typename T1, typename T2>
+Closure *NewPermanentCallback(void (*f)(T1, T2), T1 arg1, T2 arg2) { return nullptr; }
+
+void h() {
+ (void)NewCallback(&ffff, /*xxxx=*/11, /*yyyy=*/22);
+ (void)NewPermanentCallback(&ffff, /*xxxx=*/11, /*yyyy=*/22);
+}
+
+template<typename... Args>
+void variadic(Args&&... args);
+
+template<typename... Args>
+void variadic2(int zzz, Args&&... args);
+
+void templates() {
+ variadic(/*xxx=*/0, /*yyy=*/1);
+ variadic2(/*zzU=*/0, /*xxx=*/1, /*yyy=*/2);
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: argument name 'zzU' in comment does not match parameter name 'zzz'
+ // CHECK-FIXES: variadic2(/*zzz=*/0, /*xxx=*/1, /*yyy=*/2);
+}
+
+#define FALSE 0
+void qqq(bool aaa);
+void f() { qqq(/*bbb=*/FALSE); }
+// CHECK-MESSAGES: [[@LINE-1]]:16: warning: argument name 'bbb' in comment does not match parameter name 'aaa'
+// CHECK-FIXES: void f() { qqq(/*bbb=*/FALSE); }
+
+void f(bool _with_underscores_);
+void ignores_underscores() {
+ f(/*With_Underscores=*/false);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-assert-side-effect %t -- -config="{CheckOptions: [{key: misc-assert-side-effect.CheckFunctionCalls, value: 1}, {key: misc-assert-side-effect.AssertMacros, value: 'assert,assert2,my_assert,convoluted_assert,msvc_assert'}]}" -- -fexceptions
+
+//===--- assert definition block ------------------------------------------===//
+int abort() { return 0; }
+
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x) \
+ if (!(x)) \
+ (void)abort()
+#endif
+
+void print(...);
+#define assert2(e) (__builtin_expect(!(e), 0) ? \
+ print (#e, __FILE__, __LINE__) : (void)0)
+
+#ifdef NDEBUG
+#define my_assert(x) 1
+#else
+#define my_assert(x) \
+ ((void)((x) ? 1 : abort()))
+#endif
+
+#ifdef NDEBUG
+#define not_my_assert(x) 1
+#else
+#define not_my_assert(x) \
+ if (!(x)) \
+ (void)abort()
+#endif
+
+#define real_assert(x) ((void)((x) ? 1 : abort()))
+#define wrap1(x) real_assert(x)
+#define wrap2(x) wrap1(x)
+#define convoluted_assert(x) wrap2(x)
+
+#define msvc_assert(expression) (void)( \
+ (!!(expression)) || \
+ (abort(), 0) \
+ )
+
+
+//===----------------------------------------------------------------------===//
+
+class MyClass {
+public:
+ bool badFunc(int a, int b) { return a * b > 0; }
+ bool goodFunc(int a, int b) const { return a * b > 0; }
+
+ MyClass &operator=(const MyClass &rhs) { return *this; }
+
+ int operator-() { return 1; }
+
+ operator bool() const { return true; }
+
+ void operator delete(void *p) {}
+};
+
+bool freeFunction() {
+ return true;
+}
+
+int main() {
+
+ int X = 0;
+ bool B = false;
+ assert(X == 1);
+
+ assert(X = 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect [misc-assert-side-effect]
+ my_assert(X = 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found my_assert() with side effect
+ convoluted_assert(X = 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found convoluted_assert() with side effect
+ not_my_assert(X = 1);
+
+ assert(++X);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+ assert(!B);
+
+ assert(B || true);
+
+ assert(freeFunction());
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+ MyClass mc;
+ assert(mc.badFunc(0, 1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+ assert(mc.goodFunc(0, 1));
+
+ MyClass mc2;
+ assert(mc2 = mc);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+ assert(-mc > 0);
+
+ MyClass *mcp;
+ assert(mcp = new MyClass);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+ assert((delete mcp, false));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+ assert((throw 1, false));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+ assert2(1 == 2 - 1);
+
+ msvc_assert(mc2 = mc);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found msvc_assert() with side effect
+
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-bool-pointer-implicit-conversion %t
+
+bool *SomeFunction();
+void SomeOtherFunction(bool*);
+bool F();
+void G(bool);
+
+
+template <typename T>
+void t(T b) {
+ if (b) {
+ }
+}
+
+void foo() {
+ bool *b = SomeFunction();
+ if (b) {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: dubious check of 'bool *' against 'nullptr'
+// CHECK-FIXES: if (*b) {
+ }
+
+ if (F() && b) {
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dubious check of 'bool *' against 'nullptr'
+// CHECK-FIXES: if (F() && *b) {
+ }
+
+ // TODO: warn here.
+ if (b) {
+ G(b);
+ }
+
+#define TESTMACRO if (b || F())
+
+ TESTMACRO {
+ }
+
+ t(b);
+
+ if (!b) {
+ // no-warning
+ }
+
+ if (SomeFunction()) {
+ // no-warning
+ }
+
+ bool *c = SomeFunction();
+ if (c) {
+ (void)c;
+ (void)*c; // no-warning
+ }
+
+ if (c) {
+ *c = true; // no-warning
+ }
+
+ if (c) {
+ c[0] = false; // no-warning
+ }
+
+ if (c) {
+ SomeOtherFunction(c); // no-warning
+ }
+
+ if (c) {
+ delete[] c; // no-warning
+ }
+
+ if (c) {
+ *(c) = false; // no-warning
+ }
+
+ struct {
+ bool *b;
+ } d = { SomeFunction() };
+
+ if (d.b)
+ (void)*d.b; // no-warning
+
+#define CHECK(b) if (b) {}
+ CHECK(c)
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-dangling-handle %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: misc-dangling-handle.HandleClasses, \
+// RUN: value: 'std::basic_string_view; ::llvm::StringRef;'}]}" \
+// RUN: -- -std=c++11
+
+namespace std {
+
+template <typename T>
+class vector {
+ public:
+ using const_iterator = const T*;
+ using iterator = T*;
+ using size_type = int;
+
+ void assign(size_type count, const T& value);
+ iterator insert(const_iterator pos, const T& value);
+ iterator insert(const_iterator pos, T&& value);
+ iterator insert(const_iterator pos, size_type count, const T& value);
+ void push_back(const T&);
+ void push_back(T&&);
+ void resize(size_type count, const T& value);
+};
+
+template <typename, typename>
+class pair {};
+
+template <typename T>
+class set {
+ public:
+ using const_iterator = const T*;
+ using iterator = T*;
+
+ std::pair<iterator, bool> insert(const T& value);
+ std::pair<iterator, bool> insert(T&& value);
+ iterator insert(const_iterator hint, const T& value);
+ iterator insert(const_iterator hint, T&& value);
+};
+
+template <typename Key, typename Value>
+class map {
+ public:
+ using value_type = pair<Key, Value>;
+ value_type& operator[](const Key& key);
+ value_type& operator[](Key&& key);
+};
+
+class basic_string {
+ public:
+ basic_string();
+ basic_string(const char*);
+ ~basic_string();
+};
+
+typedef basic_string string;
+
+class basic_string_view {
+ public:
+ basic_string_view(const char*);
+ basic_string_view(const basic_string&);
+};
+
+typedef basic_string_view string_view;
+
+} // namespace std
+
+namespace llvm {
+
+class StringRef {
+ public:
+ StringRef();
+ StringRef(const char*);
+ StringRef(const std::string&);
+};
+
+} // namespace llvm
+
+std::string ReturnsAString();
+
+void Positives() {
+ std::string_view view1 = std::string();
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives its value [misc-dangling-handle]
+
+ std::string_view view_2 = ReturnsAString();
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+
+ view1 = std::string();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+ const std::string& str_ref = "";
+ std::string_view view3 = true ? "A" : str_ref;
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+ view3 = true ? "A" : str_ref;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+ std::string_view view4(ReturnsAString());
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+}
+
+void OtherTypes() {
+ llvm::StringRef ref = std::string();
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: llvm::StringRef outlives its value
+}
+
+const char static_array[] = "A";
+std::string_view ReturnStatements(int i, std::string value_arg,
+ const std::string &ref_arg) {
+ const char array[] = "A";
+ const char* ptr = "A";
+ std::string s;
+ static std::string ss;
+ switch (i) {
+ // Bad cases
+ case 0:
+ return array; // refers to local
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+ case 1:
+ return s; // refers to local
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+ case 2:
+ return std::string(); // refers to temporary
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+ case 3:
+ return value_arg; // refers to by-value arg
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+
+ // Ok cases
+ case 100:
+ return ss; // refers to static
+ case 101:
+ return static_array; // refers to static
+ case 102:
+ return ptr; // pointer is ok
+ case 103:
+ return ref_arg; // refers to by-ref arg
+ }
+
+ struct S {
+ std::string_view view() { return value; }
+ std::string value;
+ };
+
+ (void)[&]()->std::string_view {
+ // This should not warn. The string is bound by reference.
+ return s;
+ };
+ (void)[=]() -> std::string_view {
+ // This should not warn. The reference is valid as long as the lambda.
+ return s;
+ };
+ (void)[=]() -> std::string_view {
+ // FIXME: This one should warn. We are returning a reference to a local
+ // lambda variable.
+ std::string local;
+ return local;
+ };
+ return "";
+}
+
+void Containers() {
+ std::vector<std::string_view> v;
+ v.assign(3, std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+ v.insert(nullptr, std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+ v.insert(nullptr, 3, std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+ v.push_back(std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+ v.resize(3, std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+ std::set<std::string_view> s;
+ s.insert(std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+ s.insert(nullptr, std::string());
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+ std::map<std::string_view, int> m;
+ m[std::string()];
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+}
+
+void TakesAStringView(std::string_view);
+
+void Negatives(std::string_view default_arg = ReturnsAString()) {
+ std::string str;
+ std::string_view view = str;
+
+ TakesAStringView(std::string());
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-definitions-in-headers %t -- -- -std=c++1z
+
+class CE {
+ constexpr static int i = 5; // OK: inline variable definition.
+};
+
+inline int i = 5; // OK: inline variable definition.
+
+int b = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'b' defined in a header file; variable definitions in header files can lead to ODR violations [misc-definitions-in-headers]
--- /dev/null
+// RUN: %check_clang_tidy %s misc-definitions-in-headers %t
+
+int f() {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f' defined in a header file; function definitions in header files can lead to ODR violations [misc-definitions-in-headers]
+// CHECK-FIXES: inline int f() {
+ return 1;
+}
+
+class CA {
+ void f1() {} // OK: inline class member function definition.
+ void f2();
+ template<typename T>
+ T f3() {
+ T a = 1;
+ return a;
+ }
+ template<typename T>
+ struct CAA {
+ struct CAB {
+ void f4();
+ };
+ };
+};
+
+void CA::f2() { }
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: function 'f2' defined in a header file;
+// CHECK-FIXES: inline void CA::f2() {
+
+template <>
+int CA::f3() {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: full function template specialization 'f3<int>' defined in a header file;
+// CHECK-FIXES: inline int CA::f3() {
+ int a = 1;
+ return a;
+}
+
+template <typename T>
+void CA::CAA<T>::CAB::f4() {
+// OK: member function definition of a nested template class in a class.
+}
+
+template <typename T>
+struct CB {
+ void f1();
+ struct CCA {
+ void f2(T a);
+ };
+ struct CCB; // OK: forward declaration.
+ static int a; // OK: class static data member declaration.
+};
+
+template <typename T>
+void CB<T>::f1() { // OK: Member function definition of a class template.
+}
+
+template <typename T>
+void CB<T>::CCA::f2(T a) {
+// OK: member function definition of a nested class in a class template.
+}
+
+template <typename T>
+struct CB<T>::CCB {
+ void f3();
+};
+
+template <typename T>
+void CB<T>::CCB::f3() {
+// OK: member function definition of a nested class in a class template.
+}
+
+template <typename T>
+int CB<T>::a = 2; // OK: static data member definition of a class template.
+
+template class CB<int>; // OK: explicitly instantiated static data member of a class template.
+inline int callCB() {
+ CB<double> cb; // OK: implicitly instantiated static data member of a class template.
+ return cb.a;
+}
+
+template <typename T>
+T tf() { // OK: template function definition.
+ T a;
+ return a;
+}
+
+
+namespace NA {
+ int f() { return 1; }
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: function 'f' defined in a header file;
+// CHECK-FIXES: inline int f() { return 1; }
+}
+
+template <typename T>
+T f3() {
+ T a = 1;
+ return a;
+}
+
+template <>
+int f3() {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: full function template specialization 'f3<int>' defined in a header file;
+// CHECK-FIXES: inline int f3() {
+ int a = 1;
+ return a;
+}
+
+int f5(); // OK: function declaration.
+inline int f6() { return 1; } // OK: inline function definition.
+namespace {
+ int f7() { return 1; }
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: function 'f7' defined in a header file;
+}
+
+int f8() = delete; // OK: the function being marked delete is not callable.
+
+template <typename T>
+int f9(T t) { return 1; }
+
+inline void callF9() { f9(1); } // OK: implicitly instantiated function.
+template int f9(double); // OK: explicitly instantiated function.
+
+int a = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'a' defined in a header file; variable definitions in header files can lead to ODR violations [misc-definitions-in-headers]
+CA a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:4: warning: variable 'a1' defined in a header file;
+
+namespace NB {
+ int b = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'b' defined in a header file;
+ const int c = 1; // OK: internal linkage variable definition.
+}
+
+class CC {
+ static int d; // OK: class static data member declaration.
+};
+
+int CC::d = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: variable 'd' defined in a header file;
+
+const char* ca = "foo";
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: variable 'ca' defined in a header file;
+
+namespace {
+ int e = 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'e' defined in a header file;
+}
+
+const char* const g = "foo"; // OK: internal linkage variable definition.
+static int h = 1; // OK: internal linkage variable definition.
+const int i = 1; // OK: internal linkage variable definition.
+extern int j; // OK: internal linkage variable definition.
+
+template <typename T, typename U>
+struct CD {
+ int f();
+};
+
+template <typename T>
+struct CD<T, int> {
+ int f();
+};
+
+template <>
+struct CD<int, int> {
+ int f();
+};
+
+int CD<int, int>::f() {
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: function 'f' defined in a header file;
+// CHECK-FIXES: inline int CD<int, int>::f() {
+ return 0;
+}
+
+template <typename T>
+int CD<T, int>::f() { // OK: partial template specialization.
+ return 0;
+}
+
+constexpr int k = 1; // OK: constexpr variable has internal linkage.
+
+constexpr int f10() { return 0; } // OK: constexpr function definition.
--- /dev/null
+// RUN: %check_clang_tidy %s misc-fold-init-type %t
+
+namespace std {
+template <class InputIt, class T>
+T accumulate(InputIt first, InputIt last, T init);
+
+template <class InputIt, class T>
+T reduce(InputIt first, InputIt last, T init);
+template <class ExecutionPolicy, class InputIt, class T>
+T reduce(ExecutionPolicy &&policy,
+ InputIt first, InputIt last, T init);
+
+struct parallel_execution_policy {};
+constexpr parallel_execution_policy par{};
+
+template <class InputIt1, class InputIt2, class T>
+T inner_product(InputIt1 first1, InputIt1 last1,
+ InputIt2 first2, T value);
+
+template <class ExecutionPolicy, class InputIt1, class InputIt2, class T>
+T inner_product(ExecutionPolicy &&policy, InputIt1 first1, InputIt1 last1,
+ InputIt2 first2, T value);
+
+} // namespace std
+
+struct FloatIterator {
+ typedef float value_type;
+};
+template <typename ValueType>
+struct TypedefTemplateIterator { typedef ValueType value_type; };
+template <typename ValueType>
+struct UsingTemplateIterator { using value_type = ValueType; };
+template <typename ValueType>
+struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
+template <typename ValueType>
+struct DependentUsingTemplateIterator : public TypedefTemplateIterator<ValueType> { using typename TypedefTemplateIterator<ValueType>::value_type; };
+using TypedeffedIterator = FloatIterator;
+
+// Positives.
+
+int accumulatePositive1() {
+ float a[1] = {0.5f};
+ return std::accumulate(a, a + 1, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive2() {
+ FloatIterator it;
+ return std::accumulate(it, it, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive3() {
+ double a[1] = {0.0};
+ return std::accumulate(a, a + 1, 0.0f);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
+}
+
+int accumulatePositive4() {
+ TypedefTemplateIterator<unsigned> it;
+ return std::accumulate(it, it, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive5() {
+ UsingTemplateIterator<unsigned> it;
+ return std::accumulate(it, it, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive6() {
+ DependentTypedefTemplateIterator<UsingTemplateIterator<unsigned>> it;
+ return std::accumulate(it, it, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive7() {
+ TypedeffedIterator it;
+ return std::accumulate(it, it, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive8() {
+ DependentUsingTemplateIterator<unsigned> it;
+ return std::accumulate(it, it, 0);
+ // FIXME: this one should trigger too.
+}
+
+int reducePositive1() {
+ float a[1] = {0.5f};
+ return std::reduce(a, a + 1, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int reducePositive2() {
+ float a[1] = {0.5f};
+ return std::reduce(std::par, a, a + 1, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int innerProductPositive1() {
+ float a[1] = {0.5f};
+ int b[1] = {1};
+ return std::inner_product(std::par, a, a + 1, b, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int innerProductPositive2() {
+ float a[1] = {0.5f};
+ int b[1] = {1};
+ return std::inner_product(std::par, a, a + 1, b, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+// Negatives.
+
+int negative1() {
+ float a[1] = {0.5f};
+ // This is OK because types match.
+ return std::accumulate(a, a + 1, 0.0);
+}
+
+int negative2() {
+ float a[1] = {0.5f};
+ // This is OK because double is bigger than float.
+ return std::accumulate(a, a + 1, 0.0);
+}
+
+int negative3() {
+ float a[1] = {0.5f};
+ // This is OK because the user explicitly specified T.
+ return std::accumulate<float *, float>(a, a + 1, 0);
+}
+
+int negative4() {
+ TypedefTemplateIterator<unsigned> it;
+ // For now this is OK.
+ return std::accumulate(it, it, 0.0);
+}
+
+int negative5() {
+ float a[1] = {0.5f};
+ float b[1] = {1.0f};
+ return std::inner_product(std::par, a, a + 1, b, 0.0f);
+}
+
+namespace blah {
+namespace std {
+template <class InputIt, class T>
+T accumulate(InputIt, InputIt, T); // We should not care about this one.
+}
+
+int negative5() {
+ float a[1] = {0.5f};
+ // Note that this is using blah::std::accumulate.
+ return std::accumulate(a, a + 1, 0);
+}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-forward-declaration-namespace %t
+
+namespace {
+// This is a declaration in a wrong namespace.
+class T_A;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [misc-forward-declaration-namespace]
+// CHECK-MESSAGES: note: a declaration of 'T_A' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [misc-forward-declaration-namespace]
+// CHECK-MESSAGES: note: a definition of 'T_A' is found here
+}
+
+namespace na {
+// This is a declaration in a wrong namespace.
+class T_A;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)'
+// CHECK-MESSAGES: note: a declaration of 'T_A' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)'
+// CHECK-MESSAGES: note: a definition of 'T_A' is found here
+}
+
+class T_A;
+
+class T_A {
+ int x;
+};
+
+class NESTED;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)'
+// CHECK-MESSAGES: note: a definition of 'NESTED' is found here
+
+namespace {
+namespace nq {
+namespace {
+class NESTED {};
+}
+}
+}
+
+namespace na {
+class T_B;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb'
+// CHECK-MESSAGES: note: a declaration of 'T_B' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb'
+// CHECK-MESSAGES: note: a definition of 'T_B' is found here
+}
+
+namespace nb {
+class T_B;
+}
+
+namespace nb {
+class T_B {
+ int x;
+};
+}
+
+namespace na {
+class T_B;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb'
+// CHECK-MESSAGES: note: a declaration of 'T_B' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb'
+// CHECK-MESSAGES: note: a definition of 'T_B' is found here
+}
+
+// A simple forward declaration. Although it is never used, but no declaration
+// with the same name is found in other namespace.
+class OUTSIDER;
+
+namespace na {
+// This class is referenced declaration, we don't generate warning.
+class OUTSIDER_1;
+}
+
+void f(na::OUTSIDER_1);
+
+namespace nc {
+// This class is referenced as friend in OOP.
+class OUTSIDER_1;
+
+class OOP {
+ friend struct OUTSIDER_1;
+};
+}
+
+namespace nd {
+class OUTSIDER_1;
+void f(OUTSIDER_1 *);
+}
+
+namespace nb {
+class OUTSIDER_1;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na'
+// CHECK-MESSAGES: note: a declaration of 'OUTSIDER_1' is found here
+}
+
+
+namespace na {
+template<typename T>
+class T_C;
+}
+
+namespace nb {
+// FIXME: this is an error, but we don't consider template class declaration
+// now.
+template<typename T>
+class T_C;
+}
+
+namespace na {
+template<typename T>
+class T_C {
+ int x;
+};
+}
+
+namespace na {
+
+template <typename T>
+class T_TEMP {
+ template <typename _Tp1>
+ struct rebind { typedef T_TEMP<_Tp1> other; };
+};
+
+// We ignore class template specialization.
+template class T_TEMP<char>;
+}
+
+namespace nb {
+
+template <typename T>
+class T_TEMP_1 {
+ template <typename _Tp1>
+ struct rebind { typedef T_TEMP_1<_Tp1> other; };
+};
+
+// We ignore class template specialization.
+extern template class T_TEMP_1<char>;
+}
+
+namespace nd {
+class D;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne'
+// CHECK-MESSAGES: note: a declaration of 'D' is found here
+}
+
+namespace nd {
+namespace ne {
+class D;
+}
+}
+
+int f(nd::ne::D &d);
+
+
+// This should be ignored by the check.
+template <typename... Args>
+class Observer {
+ class Impl;
+};
+
+template <typename... Args>
+class Observer<Args...>::Impl {
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-forwarding-reference-overload %t -- -- -std=c++14
+
+namespace std {
+template <bool B, class T = void> struct enable_if { typedef T type; };
+
+template <class T> struct enable_if<true, T> { typedef T type; };
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+
+template <class T> struct enable_if_nice { typedef T type; };
+} // namespace std
+
+namespace foo {
+template <class T> struct enable_if { typedef T type; };
+} // namespace foo
+
+template <typename T> constexpr bool just_true = true;
+
+class Test1 {
+public:
+ template <typename T> Test1(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+ template <typename T> Test1(T &&n, int i = 5, ...);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+ template <typename T, typename U = typename std::enable_if_nice<T>::type>
+ Test1(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+ template <typename T>
+ Test1(T &&n, typename foo::enable_if<long>::type i = 5, ...);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+ Test1(const Test1 &other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+ Test1(Test1 &other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+ Test1(Test1 &&other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+};
+
+template <typename U> class Test2 {
+public:
+ // Two parameters without default value, can't act as copy / move constructor.
+ template <typename T, class V> Test2(T &&n, V &&m, int i = 5, ...);
+
+ // Guarded with enable_if.
+ template <typename T>
+ Test2(T &&n, int i = 5,
+ std::enable_if_t<sizeof(int) < sizeof(long), int> a = 5, ...);
+
+ // Guarded with enable_if.
+ template <typename T, typename X = typename std::enable_if<
+ sizeof(int) < sizeof(long), double>::type &>
+ Test2(T &&n);
+
+ // Guarded with enable_if.
+ template <typename T>
+ Test2(T &&n, typename std::enable_if<just_true<T>>::type **a = nullptr);
+
+ // Guarded with enable_if.
+ template <typename T, typename X = std::enable_if_t<just_true<T>> *&&>
+ Test2(T &&n, double d = 0.0);
+
+ // Not a forwarding reference parameter.
+ template <typename T> Test2(const T &&n);
+
+ // Not a forwarding reference parameter.
+ Test2(int &&x);
+
+ // Two parameters without default value, can't act as copy / move constructor.
+ template <typename T> Test2(T &&n, int x);
+
+ // Not a forwarding reference parameter.
+ template <typename T> Test2(U &&n);
+};
+
+// The copy and move constructors are both disabled.
+class Test3 {
+public:
+ template <typename T> Test3(T &&n);
+
+ template <typename T> Test3(T &&n, int I = 5, ...);
+
+ Test3(const Test3 &rhs) = delete;
+
+private:
+ Test3(Test3 &&rhs);
+};
+
+// Both the copy and the (compiler generated) move constructors can be hidden.
+class Test4 {
+public:
+ template <typename T> Test4(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+ Test4(const Test4 &rhs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+};
+
+// Nothing can be hidden, the copy constructor is implicitly deleted.
+class Test5 {
+public:
+ template <typename T> Test5(T &&n);
+
+ Test5(Test5 &&rhs) = delete;
+};
+
+// Only the move constructor can be hidden.
+class Test6 {
+public:
+ template <typename T> Test6(T &&n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the move constructor
+
+ Test6(Test6 &&rhs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+private:
+ Test6(const Test6 &rhs);
+};
+
+// Do not dereference a null BaseType.
+template <class _Callable> class result_of;
+template <class _Fp, class ..._Args> class result_of<_Fp(_Args...)> { };
+template <class _Tp> using result_of_t = typename result_of<_Tp>::type;
+
+template <class... _Types> struct __overload;
+template <class _Tp, class... _Types>
+struct __overload<_Tp, _Types...> : __overload<_Types...> {
+ using __overload<_Types...>::operator();
+};
+
+template <class _Tp, class... _Types>
+using __best_match_t = typename result_of_t<__overload<_Types...>(_Tp&&)>::type;
+
+template <class... _Types>
+class variant {
+public:
+ template <class _Arg, class _Tp = __best_match_t<_Arg, _Types...> >
+ constexpr variant(_Arg&& __arg) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-inaccurate-erase %t
+
+namespace std {
+template <typename T> struct vec_iterator {
+ T ptr;
+ vec_iterator operator++(int);
+
+ template <typename X>
+ vec_iterator(const vec_iterator<X> &); // Omit enable_if<...>.
+};
+
+template <typename T> struct vector {
+ typedef vec_iterator<T*> iterator;
+
+ iterator begin();
+ iterator end();
+
+ void erase(iterator);
+ void erase(iterator, iterator);
+};
+
+template <typename T> struct vector_with_const_iterator {
+ typedef vec_iterator<T*> iterator;
+ typedef vec_iterator<const T*> const_iterator;
+
+ iterator begin();
+ iterator end();
+
+ void erase(const_iterator);
+ void erase(const_iterator, const_iterator);
+};
+
+template <typename FwIt, typename T>
+FwIt remove(FwIt begin, FwIt end, const T &val);
+
+template <typename FwIt, typename Func>
+FwIt remove_if(FwIt begin, FwIt end, Func f);
+
+template <typename FwIt> FwIt unique(FwIt begin, FwIt end);
+
+template <typename T> struct unique_ptr {};
+} // namespace std
+
+struct custom_iter {};
+struct custom_container {
+ void erase(...);
+ custom_iter begin();
+ custom_iter end();
+};
+
+template <typename T> void g() {
+ T t;
+ t.erase(std::remove(t.begin(), t.end(), 10));
+ // CHECK-FIXES: {{^ }}t.erase(std::remove(t.begin(), t.end(), 10));{{$}}
+
+ std::vector<int> v;
+ v.erase(remove(v.begin(), v.end(), 10));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+ // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}}
+}
+
+#define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y))
+// CHECK-FIXES: #define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y))
+
+int main() {
+ std::vector<int> v;
+
+ v.erase(remove(v.begin(), v.end(), 10));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one item even when multiple items should be removed [misc-inaccurate-erase]
+ // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}}
+ v.erase(remove(v.begin(), v.end(), 20), v.end());
+
+ auto *p = &v;
+ p->erase(remove(p->begin(), p->end(), 11));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+ // CHECK-FIXES: {{^ }}p->erase(remove(p->begin(), p->end(), 11), p->end());{{$}}
+
+ std::vector_with_const_iterator<int> v2;
+ v2.erase(remove(v2.begin(), v2.end(), 12));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+ // CHECK-FIXES: {{^ }}v2.erase(remove(v2.begin(), v2.end(), 12), v2.end());{{$}}
+
+ // Fix is not trivial.
+ auto it = v.end();
+ v.erase(remove(v.begin(), it, 10));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+ // CHECK-FIXES: {{^ }}v.erase(remove(v.begin(), it, 10));{{$}}
+
+ g<std::vector<int>>();
+ g<custom_container>();
+
+ ERASE(v, 15);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: this call will remove at most one
+ // CHECK-FIXES: {{^ }}ERASE(v, 15);{{$}}
+
+ std::vector<std::unique_ptr<int>> vupi;
+ auto iter = vupi.begin();
+ vupi.erase(iter++);
+ // CHECK-FIXES: {{^ }}vupi.erase(iter++);{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-incorrect-roundings %t
+
+void b(int x) {}
+
+void f1() {
+ float f;
+ double d;
+ long double ld;
+ int x;
+
+ x = (d + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) to integer leads to incorrect rounding; consider using lround (#include <cmath>) instead [misc-incorrect-roundings]
+ x = (d + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (f + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (f + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5 + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5f + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5 + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5f + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5 + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (0.5f + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+ x = (int)(d + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(d + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(ld + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(ld + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(f + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(f + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5 + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5f + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5 + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5f + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5 + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = (int)(0.5f + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+ x = static_cast<int>(d + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(d + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(ld + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(ld + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(f + 0.5);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(f + 0.5f);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5 + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5f + d);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5 + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5f + ld);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5 + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+ x = static_cast<int>(0.5f + f);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+
+ // Don't warn if constant is not 0.5.
+ x = (int)(d + 0.6);
+ x = (int)(0.6 + d);
+
+ // Don't warn if binary operator is not directly beneath cast.
+ x = (int)(1 + (0.5 + f));
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-inefficient-algorithm %t
+
+namespace std {
+template <typename T> struct less {
+ bool operator()(const T &lhs, const T &rhs) { return lhs < rhs; }
+};
+
+template <typename T> struct greater {
+ bool operator()(const T &lhs, const T &rhs) { return lhs > rhs; }
+};
+
+struct iterator_type {};
+
+template <typename K, typename Cmp = less<K>> struct set {
+ typedef iterator_type iterator;
+ iterator find(const K &k);
+ unsigned count(const K &k);
+
+ iterator begin();
+ iterator end();
+ iterator begin() const;
+ iterator end() const;
+};
+
+struct other_iterator_type {};
+
+template <typename K, typename V, typename Cmp = less<K>> struct map {
+ typedef other_iterator_type iterator;
+ iterator find(const K &k);
+ unsigned count(const K &k);
+
+ iterator begin();
+ iterator end();
+ iterator begin() const;
+ iterator end() const;
+};
+
+template <typename K, typename V> struct multimap : map<K, V> {};
+template <typename K> struct unordered_set : set<K> {};
+template <typename K, typename V> struct unordered_map : map<K, V> {};
+template <typename K> struct unordered_multiset : set<K> {};
+template <typename K, typename V> struct unordered_multimap : map<K, V> {};
+
+template <typename K, typename Cmp = less<K>> struct multiset : set<K, Cmp> {};
+
+template <typename FwIt, typename K> FwIt find(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K, typename Cmp>
+FwIt find(FwIt, FwIt, const K &, Cmp);
+
+template <typename FwIt, typename Pred> FwIt find_if(FwIt, FwIt, Pred);
+
+template <typename FwIt, typename K> FwIt count(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K> FwIt lower_bound(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K, typename Ord>
+FwIt lower_bound(FwIt, FwIt, const K &, Ord);
+}
+
+#define FIND_IN_SET(x) find(x.begin(), x.end(), 10)
+// CHECK-FIXES: #define FIND_IN_SET(x) find(x.begin(), x.end(), 10)
+
+template <typename T> void f(const T &t) {
+ std::set<int> s;
+ find(s.begin(), s.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}s.find(46);{{$}}
+
+ find(t.begin(), t.end(), 46);
+ // CHECK-FIXES: {{^ }}find(t.begin(), t.end(), 46);{{$}}
+}
+
+int main() {
+ std::set<int> s;
+ auto it = std::find(s.begin(), s.end(), 43);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: this STL algorithm call should be replaced with a container method [misc-inefficient-algorithm]
+ // CHECK-FIXES: {{^ }}auto it = s.find(43);{{$}}
+ auto c = count(s.begin(), s.end(), 43);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}auto c = s.count(43);{{$}}
+
+#define SECOND(x, y, z) y
+ SECOND(q,std::count(s.begin(), s.end(), 22),w);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}SECOND(q,s.count(22),w);{{$}}
+
+ it = find_if(s.begin(), s.end(), [](int) { return false; });
+
+ std::multiset<int> ms;
+ find(ms.begin(), ms.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}ms.find(46);{{$}}
+
+ const std::multiset<int> &msref = ms;
+ find(msref.begin(), msref.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}msref.find(46);{{$}}
+
+ std::multiset<int> *msptr = &ms;
+ find(msptr->begin(), msptr->end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}msptr->find(46);{{$}}
+
+ it = std::find(s.begin(), s.end(), 43, std::greater<int>());
+ // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: different comparers used in the algorithm and the container [misc-inefficient-algorithm]
+
+ FIND_IN_SET(s);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}FIND_IN_SET(s);{{$}}
+
+ f(s);
+
+ std::unordered_set<int> us;
+ lower_bound(us.begin(), us.end(), 10);
+ // CHECK-FIXES: {{^ }}lower_bound(us.begin(), us.end(), 10);{{$}}
+ find(us.begin(), us.end(), 10);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}us.find(10);{{$}}
+
+ std::unordered_multiset<int> ums;
+ find(ums.begin(), ums.end(), 10);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}ums.find(10);{{$}}
+
+ std::map<int, int> intmap;
+ find(intmap.begin(), intmap.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}find(intmap.begin(), intmap.end(), 46);{{$}}
+
+ std::multimap<int, int> intmmap;
+ find(intmmap.begin(), intmmap.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}find(intmmap.begin(), intmmap.end(), 46);{{$}}
+
+ std::unordered_map<int, int> umap;
+ find(umap.begin(), umap.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}find(umap.begin(), umap.end(), 46);{{$}}
+
+ std::unordered_multimap<int, int> ummap;
+ find(ummap.begin(), ummap.end(), 46);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}find(ummap.begin(), ummap.end(), 46);{{$}}
+}
+
+struct Value {
+ int value;
+};
+
+struct Ordering {
+ bool operator()(const Value &lhs, const Value &rhs) const {
+ return lhs.value < rhs.value;
+ }
+ bool operator()(int lhs, const Value &rhs) const { return lhs < rhs.value; }
+};
+
+void g(std::set<Value, Ordering> container, int value) {
+ lower_bound(container.begin(), container.end(), value, Ordering());
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+ // CHECK-FIXES: {{^ }}lower_bound(container.begin(), container.end(), value, Ordering());{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-lambda-function-name %t
+
+void Foo(const char* a, const char* b, int c) {}
+
+#define FUNC_MACRO Foo(__func__, "", 0)
+#define FUNCTION_MACRO Foo(__FUNCTION__, "", 0)
+#define EMBED_IN_ANOTHER_MACRO1 FUNC_MACRO
+
+void Positives() {
+ [] { __func__; }();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+ [] { __FUNCTION__; }();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+ [] { FUNC_MACRO; }();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+ [] { FUNCTION_MACRO; }();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+ [] { EMBED_IN_ANOTHER_MACRO1; }();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+}
+
+#define FUNC_MACRO_WITH_FILE_AND_LINE Foo(__func__, __FILE__, __LINE__)
+#define FUNCTION_MACRO_WITH_FILE_AND_LINE Foo(__FUNCTION__, __FILE__, __LINE__)
+#define EMBED_IN_ANOTHER_MACRO2 FUNC_MACRO_WITH_FILE_AND_LINE
+
+void Negatives() {
+ __func__;
+ __FUNCTION__;
+
+ // __PRETTY_FUNCTION__ should not trigger a warning because its value is
+ // actually potentially useful.
+ __PRETTY_FUNCTION__;
+ [] { __PRETTY_FUNCTION__; }();
+
+ // Don't warn if __func__/__FUNCTION is used inside a macro that also uses
+ // __FILE__ and __LINE__, on the assumption that __FILE__ and __LINE__ will
+ // be useful even if __func__/__FUNCTION__ is not.
+ [] { FUNC_MACRO_WITH_FILE_AND_LINE; }();
+ [] { FUNCTION_MACRO_WITH_FILE_AND_LINE; }();
+ [] { EMBED_IN_ANOTHER_MACRO2; }();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-macro-parentheses %t -- -- -DVAL=0+0
+
+// The previous command-line is producing warnings and fixes with the source
+// locations from a virtual buffer. VAL is replaced by '0+0'.
+// Fixes could not be applied and should not be reported.
+int foo() { return VAL; }
+
+#define V 0+0
+int bar() { return V; }
+// CHECK-FIXES: #define V (0+0)
--- /dev/null
+// RUN: %check_clang_tidy %s misc-macro-parentheses %t
+
+#define BAD1 -1
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: macro replacement list should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD2 1+2
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: macro replacement list should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD3(A) (A+1)
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD4(x) ((unsigned char)(x & 0xff))
+// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD5(X) A*B=(C*)X+2
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+
+#define GOOD1 1
+#define GOOD2 (1+2)
+#define GOOD3(A) #A
+#define GOOD4(A,B) A ## B
+#define GOOD5(T) ((T*)0)
+#define GOOD6(B) "A" B "C"
+#define GOOD7(b) A b
+#define GOOD8(a) a B
+#define GOOD9(type) (type(123))
+#define GOOD10(car, ...) car
+#define GOOD11 a[b+c]
+#define GOOD12(x) a[x]
+#define GOOD13(x) a.x
+#define GOOD14(x) a->x
+#define GOOD15(x) ({ int a = x; a+4; })
+#define GOOD16(x) a_ ## x, b_ ## x = c_ ## x - 1,
+#define GOOD17 case 123: x=4+5; break;
+#define GOOD18(x) ;x;
+#define GOOD19 ;-2;
+#define GOOD20 void*
+#define GOOD21(a) case Fred::a:
+#define GOOD22(a) if (verbose) return a;
+#define GOOD23(type) (type::Field)
+#define GOOD24(t) std::set<t> s
+#define GOOD25(t) std::set<t,t,t> s
+#define GOOD26(x) (a->*x)
+#define GOOD27(x) (a.*x)
+#define GOOD28(x) namespace x {int b;}
+#define GOOD29(...) std::cout << __VA_ARGS__;
+#define GOOD30(args...) std::cout << args;
+#define GOOD31(X) A*X=2
+#define GOOD32(X) std::vector<X>
+
+// These are allowed for now..
+#define MAYBE1 *12.34
+#define MAYBE2 <<3
--- /dev/null
+// RUN: %check_clang_tidy %s misc-macro-repeated-side-effects %t
+
+#define badA(x,y) ((x)+((x)+(y))+(y))
+void bad(int ret, int a, int b) {
+ ret = badA(a++, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' are repeated in macro expansion [misc-macro-repeated-side-effects]
+ ret = badA(++a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+ ret = badA(a--, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+ ret = badA(--a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+ ret = badA(a, b++);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+ ret = badA(a, ++b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+ ret = badA(a, b--);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+ ret = badA(a, --b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+}
+
+
+#define MIN(A,B) ((A) < (B) ? (A) : (B)) // single ?:
+#define LIMIT(X,A,B) ((X) < (A) ? (A) : ((X) > (B) ? (B) : (X))) // two ?:
+void question(int x) {
+ MIN(x++, 12);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: side effects in the 1st macro argument 'A'
+ MIN(34, x++);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: side effects in the 2nd macro argument 'B'
+ LIMIT(x++, 0, 100);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: side effects in the 1st macro argument 'X'
+ LIMIT(20, x++, 100);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: side effects in the 2nd macro argument 'A'
+ LIMIT(20, 0, x++);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: side effects in the 3rd macro argument 'B'
+}
+
+// False positive: Repeated side effects is intentional.
+// It is hard to know when it's done by intention so right now we warn.
+#define UNROLL(A) {A A}
+void fp1(int i) {
+ UNROLL({ i++; });
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: side effects in the 1st macro argument 'A'
+}
+
+// Do not produce a false positive on a strchr() macro. Explanation; Currently the '?'
+// triggers the test to bail out, because it cannot evaluate __builtin_constant_p(c).
+# define strchrs(s, c) \
+ (__extension__ (__builtin_constant_p (c) && !__builtin_constant_p (s) \
+ && (c) == '\0' \
+ ? (char *) __rawmemchr (s, c) \
+ : __builtin_strchr (s, c)))
+char* __rawmemchr(char* a, char b) {
+ return a;
+}
+void pass(char* pstr, char ch) {
+ strchrs(pstr, ch++); // No error.
+}
+
+// Check large arguments (t=20, u=21).
+#define largeA(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, x, y, z) \
+ ((a) + (a) + (b) + (b) + (c) + (c) + (d) + (d) + (e) + (e) + (f) + (f) + (g) + (g) + \
+ (h) + (h) + (i) + (i) + (j) + (j) + (k) + (k) + (l) + (l) + (m) + (m) + (n) + (n) + \
+ (o) + (o) + (p) + (p) + (q) + (q) + (r) + (r) + (s) + (s) + (t) + (t) + (u) + (u) + \
+ (v) + (v) + (x) + (x) + (y) + (y) + (z) + (z))
+void large(int a) {
+ largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:64: warning: side effects in the 19th macro argument 's'
+ largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:67: warning: side effects in the 20th macro argument 't'
+ largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:70: warning: side effects in the 21st macro argument 'u'
+}
+
+// Passing macro argument as argument to __builtin_constant_p and macros.
+#define builtinbad(x) (__builtin_constant_p(x) + (x) + (x))
+#define builtingood1(x) (__builtin_constant_p(x) + (x))
+#define builtingood2(x) ((__builtin_constant_p(x) && (x)) || (x))
+#define macrobad(x) (builtingood1(x) + (x) + (x))
+#define macrogood(x) (builtingood1(x) + (x))
+void builtins(int ret, int a) {
+ ret += builtinbad(a++);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: side effects in the 1st macro argument 'x'
+
+ ret += builtingood1(a++);
+ ret += builtingood2(a++);
+
+ ret += macrobad(a++);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: side effects in the 1st macro argument 'x'
+
+ ret += macrogood(a++);
+}
+
+// Bail out for conditionals.
+#define condB(x,y) if(x) {x=y;} else {x=y + 1;}
+void conditionals(int a, int b)
+{
+ condB(a, b++);
+}
+
+void log(const char *s, int v);
+#define LOG(val) log(#val, (val))
+void test_log(int a) {
+ LOG(a++);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-misplaced-const %t
+
+typedef int plain_i;
+typedef int *ip;
+typedef const int *cip;
+
+typedef void (*func_ptr)(void);
+
+void func(void) {
+ // ok
+ const int *i0 = 0;
+ const plain_i *i1 = 0;
+ const cip i2 = 0; // const applies to both pointer and pointee.
+
+ // Not ok
+ const ip i3 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i3' declared with a const-qualified typedef type; results in the type being 'int *const' instead of 'const int *'
+
+ ip const i4 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i4' declared with a const-qualified
+
+ const volatile ip i5 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'i5' declared with a const-qualified typedef type; results in the type being 'int *const volatile' instead of 'const int *volatile'
+}
+
+void func2(const plain_i *i1,
+ const cip i2,
+ const ip i3,
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'i3' declared with a const-qualified
+ const int *i4) {
+}
+
+struct S {
+ const int *i0;
+ const plain_i *i1;
+ const cip i2;
+ const ip i3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i3' declared with a const-qualified
+};
+
+// Function pointers should not be diagnosed because a function
+// pointer type can never be const.
+void func3(const func_ptr fp) {
+ const func_ptr fp2 = fp;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-misplaced-const %t
+
+typedef int plain_i;
+typedef int *ip;
+typedef const int *cip;
+
+void func() {
+ if (const int *i = 0)
+ ;
+ if (const plain_i *i = 0)
+ ;
+ if (const cip i = 0)
+ ;
+
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: 'i' declared with a const-qualified typedef type; results in the type being 'int *const' instead of 'const int *'
+ if (const ip i = 0)
+ ;
+}
+
+template <typename Ty>
+struct S {
+ const Ty *i;
+ const Ty &i2;
+};
+
+template struct S<int>;
+template struct S<ip>; // ok
+template struct S<cip>;
+
+template <typename Ty>
+struct U {
+ const Ty *i;
+ const Ty &i2;
+};
+
+template struct U<int *>; // ok
+
+struct T {
+ typedef void (T::*PMF)();
+
+ void f() {
+ const PMF val = &T::f; // ok
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-misplaced-widening-cast %t -- -config="{CheckOptions: [{key: misc-misplaced-widening-cast.CheckImplicitCasts, value: 0}]}" --
+
+void func(long arg) {}
+
+void assign(int a, int b) {
+ long l;
+
+ l = a * b;
+ l = (long)(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
+ l = (long)a * b;
+
+ l = a << 8;
+ l = (long)(a << 8);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = (long)b << 8;
+
+ l = static_cast<long>(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+}
+
+void compare(int a, int b, long c) {
+ bool l;
+
+ l = a * b == c;
+ l = c == a * b;
+ l = (long)(a * b) == c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = c == (long)(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ l = (long)a * b == c;
+ l = c == (long)a * b;
+}
+
+void init(unsigned int n) {
+ long l1 = n << 8;
+ long l2 = (long)(n << 8);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+ long l3 = (long)n << 8;
+}
+
+void call(unsigned int n) {
+ func(n << 8);
+ func((long)(n << 8));
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+ func((long)n << 8);
+}
+
+long ret(int a) {
+ if (a < 0) {
+ return a * 1000;
+ } else if (a > 0) {
+ return (long)(a * 1000);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ } else {
+ return (long)a * 1000;
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-misplaced-widening-cast %t -- -config="{CheckOptions: [{key: misc-misplaced-widening-cast.CheckImplicitCasts, value: 1}]}" --
+
+void func(long arg) {}
+
+void assign(int a, int b) {
+ long l;
+
+ l = a * b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
+ l = (long)(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = (long)a * b;
+
+ l = a << 8;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = (long)(a << 8);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = (long)b << 8;
+
+ l = static_cast<long>(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+}
+
+void compare(int a, int b, long c) {
+ bool l;
+
+ l = a * b == c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = c == a * b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ l = (long)(a * b) == c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+ l = c == (long)(a * b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ l = (long)a * b == c;
+ l = c == (long)a * b;
+}
+
+void init(unsigned int n) {
+ long l1 = n << 8;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+ long l2 = (long)(n << 8);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+ long l3 = (long)n << 8;
+}
+
+void call(unsigned int n) {
+ func(n << 8);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+ func((long)(n << 8));
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+ func((long)n << 8);
+}
+
+long ret(int a) {
+ if (a < 0) {
+ return a * 1000;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ } else if (a > 0) {
+ return (long)(a * 1000);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+ } else {
+ return (long)a * 1000;
+ }
+}
+
+void dontwarn1(unsigned char a, int i, unsigned char *p) {
+ long l;
+ // The result is a 9 bit value, there is no truncation in the implicit cast.
+ l = (long)(a + 15);
+ // The result is a 12 bit value, there is no truncation in the implicit cast.
+ l = (long)(a << 4);
+ // The result is a 3 bit value, there is no truncation in the implicit cast.
+ l = (long)((i % 5) + 1);
+ // The result is a 16 bit value, there is no truncation in the implicit cast.
+ l = (long)(((*p) << 8) + *(p + 1));
+}
+
+template <class T> struct DontWarn2 {
+ void assign(T a, T b) {
+ long l;
+ l = (long)(a * b);
+ }
+};
+DontWarn2<int> DW2;
+
+// Cast is not suspicious when casting macro.
+#define A (X<<2)
+long macro1(int X) {
+ return (long)A;
+}
+
+// Don't warn about cast in macro.
+#define B(X,Y) (long)(X*Y)
+long macro2(int x, int y) {
+ return B(x,y);
+}
+
+void floatingpoint(float a, float b) {
+ double d = (double)(a * b); // Currently we don't warn for this.
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-move-const-arg %t
+
+namespace std {
+template <typename> struct remove_reference;
+
+template <typename _Tp> struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t);
+
+} // namespace std
+
+class A {
+public:
+ A() {}
+ A(const A &rhs) {}
+ A(A &&rhs) {}
+};
+
+int f1() {
+ return std::move(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the expression of the trivially-copyable type 'int' has no effect; remove std::move() [misc-move-const-arg]
+ // CHECK-FIXES: return 42;
+}
+
+int f2(int x2) {
+ return std::move(x2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x2' of the trivially-copyable type 'int'
+ // CHECK-FIXES: return x2;
+}
+
+int *f3(int *x3) {
+ return std::move(x3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x3' of the trivially-copyable type 'int *'
+ // CHECK-FIXES: return x3;
+}
+
+A f4(A x4) { return std::move(x4); }
+
+A f5(const A x5) {
+ return std::move(x5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x5' has no effect; remove std::move() or make the variable non-const [misc-move-const-arg]
+ // CHECK-FIXES: return x5;
+}
+
+template <typename T> T f6(const T x6) { return std::move(x6); }
+
+void f7() { int a = f6(10); }
+
+#define M1(x) x
+void f8() {
+ const A a;
+ M1(A b = std::move(a);)
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: std::move of the const variable 'a' has no effect; remove std::move() or make the variable non-const
+ // CHECK-FIXES: M1(A b = a;)
+}
+
+#define M2(x) std::move(x)
+int f9() { return M2(1); }
+
+template <typename T> T f10(const int x10) {
+ return std::move(x10);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x10' of the trivially-copyable type 'const int' has no effect; remove std::move() [misc-move-const-arg]
+ // CHECK-FIXES: return x10;
+}
+void f11() {
+ f10<int>(1);
+ f10<double>(1);
+}
+
+class NoMoveSemantics {
+ public:
+ NoMoveSemantics();
+ NoMoveSemantics(const NoMoveSemantics &);
+
+ NoMoveSemantics &operator=(const NoMoveSemantics &);
+};
+
+void callByConstRef(const NoMoveSemantics &);
+void callByConstRef(int i, const NoMoveSemantics &);
+
+void moveToConstReferencePositives() {
+ NoMoveSemantics obj;
+
+ // Basic case.
+ callByConstRef(std::move(obj));
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
+ // CHECK-FIXES: callByConstRef(obj);
+
+ // Also works for second argument.
+ callByConstRef(1, std::move(obj));
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: passing result of std::move() as
+ // CHECK-FIXES: callByConstRef(1, obj);
+
+ // Works if std::move() applied to a temporary.
+ callByConstRef(std::move(NoMoveSemantics()));
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
+ // CHECK-FIXES: callByConstRef(NoMoveSemantics());
+
+ // Works if calling a copy constructor.
+ NoMoveSemantics other(std::move(obj));
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: passing result of std::move() as
+ // CHECK-FIXES: NoMoveSemantics other(obj);
+
+ // Works if calling assignment operator.
+ other = std::move(obj);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: passing result of std::move() as
+ // CHECK-FIXES: other = obj;
+}
+
+class MoveSemantics {
+ public:
+ MoveSemantics();
+ MoveSemantics(MoveSemantics &&);
+
+ MoveSemantics &operator=(MoveSemantics &&);
+};
+
+void callByValue(MoveSemantics);
+
+void callByRValueRef(MoveSemantics &&);
+
+template <class T>
+void templateFunction(T obj) {
+ T other = std::move(obj);
+}
+
+#define M3(T, obj) \
+ do { \
+ T other = std::move(obj); \
+ } while (true)
+
+#define CALL(func) (func)()
+
+void moveToConstReferenceNegatives() {
+ // No warning when actual move takes place.
+ MoveSemantics move_semantics;
+ callByValue(std::move(move_semantics));
+ callByRValueRef(std::move(move_semantics));
+ MoveSemantics other(std::move(move_semantics));
+ other = std::move(move_semantics);
+
+ // No warning if std::move() not used.
+ NoMoveSemantics no_move_semantics;
+ callByConstRef(no_move_semantics);
+
+ // No warning if instantiating a template.
+ templateFunction(no_move_semantics);
+
+ // No warning inside of macro expansions.
+ M3(NoMoveSemantics, no_move_semantics);
+
+ // No warning inside of macro expansion, even if the macro expansion is inside
+ // a lambda that is, in turn, an argument to a macro.
+ CALL([no_move_semantics] { M3(NoMoveSemantics, no_move_semantics); });
+
+ auto lambda = [] {};
+ auto lambda2 = std::move(lambda);
+}
+
+class MoveOnly {
+public:
+ MoveOnly(const MoveOnly &other) = delete;
+ MoveOnly &operator=(const MoveOnly &other) = delete;
+ MoveOnly(MoveOnly &&other) = default;
+ MoveOnly &operator=(MoveOnly &&other) = default;
+};
+template <class T>
+void Q(T);
+void moveOnlyNegatives(MoveOnly val) {
+ Q(std::move(val));
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-move-constructor-init,modernize-pass-by-value %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: modernize-pass-by-value.ValuesOnly, value: 1}]}' \
+// RUN: -- -std=c++11 -isystem %S/Inputs/Headers
+
+#include <s.h>
+
+// CHECK-FIXES: #include <utility>
+
+template <class T> struct remove_reference {typedef T type;};
+template <class T> struct remove_reference<T&> {typedef T type;};
+template <class T> struct remove_reference<T&&> {typedef T type;};
+
+template <typename T>
+typename remove_reference<T>::type&& move(T&& arg) {
+ return static_cast<typename remove_reference<T>::type&&>(arg);
+}
+
+struct C {
+ C() = default;
+ C(const C&) = default;
+};
+
+struct B {
+ B() {}
+ B(const B&) {}
+ B(B &&) {}
+};
+
+struct D : B {
+ D() : B() {}
+ D(const D &RHS) : B(RHS) {}
+ // CHECK-MESSAGES: :[[@LINE+3]]:16: warning: move constructor initializes base class by calling a copy constructor [misc-move-constructor-init]
+ // CHECK-MESSAGES: 26:3: note: copy constructor being called
+ // CHECK-MESSAGES: 27:3: note: candidate move constructor here
+ D(D &&RHS) : B(RHS) {}
+};
+
+struct E : B {
+ E() : B() {}
+ E(const E &RHS) : B(RHS) {}
+ E(E &&RHS) : B(move(RHS)) {} // ok
+};
+
+struct F {
+ C M;
+
+ F(F &&) : M(C()) {} // ok
+};
+
+struct G {
+ G() = default;
+ G(const G&) = default;
+ G(G&&) = delete;
+};
+
+struct H : G {
+ H() = default;
+ H(const H&) = default;
+ H(H &&RHS) : G(RHS) {} // ok
+};
+
+struct I {
+ I(const I &) = default; // suppresses move constructor creation
+};
+
+struct J : I {
+ J(J &&RHS) : I(RHS) {} // ok
+};
+
+struct K {}; // Has implicit copy and move constructors, is trivially copyable
+struct L : K {
+ L(L &&RHS) : K(RHS) {} // ok
+};
+
+struct M {
+ B Mem;
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: move constructor initializes class member by calling a copy constructor [misc-move-constructor-init]
+ M(M &&RHS) : Mem(RHS.Mem) {}
+};
+
+struct N {
+ B Mem;
+ N(N &&RHS) : Mem(move(RHS.Mem)) {}
+};
+
+struct O {
+ O(O&& other) : b(other.b) {} // ok
+ const B b;
+};
+
+struct P {
+ P(O&& other) : b(other.b) {} // ok
+ B b;
+};
+
+struct Movable {
+ Movable(Movable &&) = default;
+ Movable(const Movable &) = default;
+ Movable &operator=(const Movable &) = default;
+ ~Movable() {}
+};
+
+struct TriviallyCopyable {
+ TriviallyCopyable() = default;
+ TriviallyCopyable(TriviallyCopyable &&) = default;
+ TriviallyCopyable(const TriviallyCopyable &) = default;
+};
+
+struct Positive {
+ Positive(Movable M) : M_(M) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: pass by value and use std::move [modernize-pass-by-value]
+ // CHECK-FIXES: Positive(Movable M) : M_(std::move(M)) {}
+ Movable M_;
+};
+
+struct NegativeMultipleInitializerReferences {
+ NegativeMultipleInitializerReferences(Movable M) : M_(M), n_(M) {}
+ Movable M_;
+ Movable n_;
+};
+
+struct NegativeReferencedInConstructorBody {
+ NegativeReferencedInConstructorBody(Movable M) : M_(M) { M_ = M; }
+ Movable M_;
+};
+
+struct NegativeParamTriviallyCopyable {
+ NegativeParamTriviallyCopyable(TriviallyCopyable T) : T_(T) {}
+ NegativeParamTriviallyCopyable(int I) : I_(I) {}
+
+ TriviallyCopyable T_;
+ int I_;
+};
+
+struct NegativeNotPassedByValue {
+ // This const ref constructor isn't warned about because the ValuesOnly option is set.
+ NegativeNotPassedByValue(const Movable &M) : M_(M) {}
+ NegativeNotPassedByValue(const Movable M) : M_(M) {}
+ NegativeNotPassedByValue(Movable &M) : M_(M) {}
+ NegativeNotPassedByValue(Movable *M) : M_(*M) {}
+ NegativeNotPassedByValue(const Movable *M) : M_(*M) {}
+ Movable M_;
+};
+
+struct Immovable {
+ Immovable(const Immovable &) = default;
+ Immovable(Immovable &&) = delete;
+};
+
+struct NegativeImmovableParameter {
+ NegativeImmovableParameter(Immovable I) : I_(I) {}
+ Immovable I_;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-move-forwarding-reference %t -- -- -std=c++14 -fno-delayed-template-parsing
+
+namespace std {
+template <typename> struct remove_reference;
+
+template <typename _Tp> struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t);
+
+} // namespace std
+
+// Standard case.
+template <typename T, typename U> void f1(U &&SomeU) {
+ T SomeT(std::move(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+}
+
+// Ignore parentheses around the argument to std::move().
+template <typename T, typename U> void f2(U &&SomeU) {
+ T SomeT(std::move((SomeU)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(std::forward<U>((SomeU)));
+}
+
+// Handle the case correctly where std::move() is being used through a using
+// declaration.
+template <typename T, typename U> void f3(U &&SomeU) {
+ using std::move;
+ T SomeT(move(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+}
+
+// Handle the case correctly where a global specifier is prepended to
+// std::move().
+template <typename T, typename U> void f4(U &&SomeU) {
+ T SomeT(::std::move(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(::std::forward<U>(SomeU));
+}
+
+// Create a correct fix if there are spaces around the scope resolution
+// operator.
+template <typename T, typename U> void f5(U &&SomeU) {
+ {
+ T SomeT(:: std :: move(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(::std::forward<U>(SomeU));
+ }
+ {
+ T SomeT(std :: move(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to
+ // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+ }
+}
+
+// Ignore const rvalue reference parameters.
+template <typename T, typename U> void f6(const U &&SomeU) {
+ T SomeT(std::move(SomeU));
+}
+
+// Ignore the case where the argument to std::move() is a lambda parameter (and
+// thus not actually a parameter of the function template).
+template <typename T, typename U> void f7() {
+ [](U &&SomeU) { T SomeT(std::move(SomeU)); };
+}
+
+// Ignore the case where the argument is a lvalue reference.
+template <typename T, typename U> void f8(U &SomeU) {
+ T SomeT(std::move(SomeU));
+}
+
+// Ignore the case where the template parameter is a class template parameter
+// (i.e. no template argument deduction is taking place).
+template <typename T, typename U> class SomeClass {
+ void f(U &&SomeU) { T SomeT(std::move(SomeU)); }
+};
+
+// Ignore the case where the function parameter in the template isn't an rvalue
+// reference but the template argument is explicitly set to be an rvalue
+// reference.
+class A {};
+template <typename T> void foo(T);
+void f8() {
+ A a;
+ foo<A &&>(std::move(a));
+}
+
+// A warning is output, but no fix is suggested, if a macro is used to rename
+// std::move.
+#define MOVE(x) std::move((x))
+template <typename T, typename U> void f9(U &&SomeU) {
+ T SomeT(MOVE(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Same result if the argument is passed outside of the macro.
+#undef MOVE
+#define MOVE std::move
+template <typename T, typename U> void f10(U &&SomeU) {
+ T SomeT(MOVE(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Same result if the macro does not include the "std" namespace.
+#undef MOVE
+#define MOVE move
+template <typename T, typename U> void f11(U &&SomeU) {
+ T SomeT(std::MOVE(SomeU));
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Handle the case correctly where the forwarding reference is a parameter of a
+// generic lambda.
+template <typename T> void f12() {
+ [] (auto&& x) { T SomeT(std::move(x)); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: forwarding reference passed to
+ // CHECK-FIXES: [] (auto&& x) { T SomeT(std::forward<decltype(x)>(x)); }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-multiple-statement-macro %t
+
+void F();
+
+#define BAD_MACRO(x) \
+ F(); \
+ F()
+
+#define GOOD_MACRO(x) \
+ do { \
+ F(); \
+ F(); \
+ } while (0)
+
+#define GOOD_MACRO2(x) F()
+
+#define GOOD_MACRO3(x) F();
+
+#define MACRO_ARG_MACRO(X) \
+ if (54) \
+ X(2)
+
+#define ALL_IN_MACRO(X) \
+ if (43) \
+ F(); \
+ F()
+
+#define GOOD_NESTED(x) \
+ if (x) \
+ GOOD_MACRO3(x); \
+ F();
+
+#define IF(x) if(x)
+
+void positives() {
+ if (1)
+ BAD_MACRO(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used without braces; some statements will be unconditionally executed [misc-multiple-statement-macro]
+ if (1) {
+ } else
+ BAD_MACRO(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+ while (1)
+ BAD_MACRO(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+ for (;;)
+ BAD_MACRO(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+
+ MACRO_ARG_MACRO(BAD_MACRO);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used
+ MACRO_ARG_MACRO(F(); int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used
+ IF(1) BAD_MACRO(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple statement macro used
+}
+
+void negatives() {
+ if (1) {
+ BAD_MACRO(1);
+ } else {
+ BAD_MACRO(1);
+ }
+ while (1) {
+ BAD_MACRO(1);
+ }
+ for (;;) {
+ BAD_MACRO(1);
+ }
+
+ if (1)
+ GOOD_MACRO(1);
+ if (1) {
+ GOOD_MACRO(1);
+ }
+ if (1)
+ GOOD_MACRO2(1);
+ if (1)
+ GOOD_MACRO3(1);
+
+ MACRO_ARG_MACRO(GOOD_MACRO);
+ ALL_IN_MACRO(1);
+
+ IF(1) GOOD_MACRO(1);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-new-delete-overloads %t -- -- -std=c++14 -fsized-deallocation
+
+typedef decltype(sizeof(int)) size_t;
+
+struct S {
+ // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope [misc-new-delete-overloads]
+ void operator delete(void *ptr, size_t) noexcept; // not a placement delete
+};
+
+struct T {
+ // Because we have enabled sized deallocations explicitly, this new/delete
+ // pair matches.
+ void *operator new(size_t size) noexcept;
+ void operator delete(void *ptr, size_t) noexcept; // ok because sized deallocation is enabled
+};
+
+// While we're here, check that global operator delete with no operator new
+// is also matched.
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope
+void operator delete(void *ptr) noexcept;
--- /dev/null
+// RUN: %check_clang_tidy %s misc-new-delete-overloads %t -- -- -std=c++14
+
+typedef decltype(sizeof(int)) size_t;
+
+struct S {
+ // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope [misc-new-delete-overloads]
+ void *operator new(size_t size) noexcept;
+ // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new[]' has no matching declaration of 'operator delete[]' at the same scope
+ void *operator new[](size_t size) noexcept;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+void *operator new(size_t size) noexcept(false);
+
+struct T {
+ // Sized deallocations are not enabled by default, and so this new/delete pair
+ // does not match. However, we expect only one warning, for the new, because
+ // the operator delete is a placement delete and we do not warn on mismatching
+ // placement operations.
+ // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+ void *operator new(size_t size) noexcept;
+ void operator delete(void *ptr, size_t) noexcept; // ok only if sized deallocation is enabled
+};
+
+struct U {
+ void *operator new(size_t size) noexcept;
+ void operator delete(void *ptr) noexcept;
+
+ void *operator new[](size_t) noexcept;
+ void operator delete[](void *) noexcept;
+};
+
+struct Z {
+ // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope
+ void operator delete(void *ptr) noexcept;
+ // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete[]' has no matching declaration of 'operator new[]' at the same scope
+ void operator delete[](void *ptr) noexcept;
+};
+
+struct A {
+ void *operator new(size_t size, Z) noexcept; // ok, placement new
+};
+
+struct B {
+ void operator delete(void *ptr, A) noexcept; // ok, placement delete
+};
+
+// It is okay to have a class with an inaccessible free store operator.
+struct C {
+ void *operator new(size_t, A) noexcept; // ok, placement new
+private:
+ void operator delete(void *) noexcept;
+};
+
+// It is also okay to have a class with a delete free store operator.
+struct D {
+ void *operator new(size_t, A) noexcept; // ok, placement new
+ void operator delete(void *) noexcept = delete;
+};
+
+struct E : U {
+ void *operator new(size_t) noexcept; // okay, we inherit operator delete from U
+};
+
+struct F : S {
+ // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+ void *operator new(size_t) noexcept;
+};
+
+class G {
+ void operator delete(void *) noexcept;
+};
+
+struct H : G {
+ // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+ void *operator new(size_t) noexcept; // base class operator is inaccessible
+};
+
+template <typename Base> struct Derived : Base {
+ void operator delete(void *);
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-noexcept-move-constructor %t
+
+class A {
+ A(A &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [misc-noexcept-move-constructor]
+ A &operator=(A &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should
+};
+
+struct B {
+ static constexpr bool kFalse = false;
+ B(B &&) noexcept(kFalse);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [misc-noexcept-move-constructor]
+};
+
+class OK {};
+
+void f() {
+ OK a;
+ a = OK();
+}
+
+class OK1 {
+ public:
+ OK1();
+ OK1(const OK1 &);
+ OK1(OK1&&) noexcept;
+ OK1 &operator=(OK1 &&) noexcept;
+ void f();
+ void g() noexcept;
+};
+
+class OK2 {
+ static constexpr bool kTrue = true;
+
+public:
+ OK2(OK2 &&) noexcept(true) {}
+ OK2 &operator=(OK2 &&) noexcept(kTrue) { return *this; }
+};
+
+struct OK3 {
+ OK3(OK3 &&) noexcept(false) {}
+ OK3 &operator=(OK3 &&) = delete;
+};
+
+struct OK4 {
+ OK4(OK4 &&) noexcept = default;
+ OK4 &operator=(OK4 &&) noexcept = default;
+};
+
+struct OK5 {
+ OK5(OK5 &&) noexcept(true) = default;
+ OK5 &operator=(OK5 &&) noexcept(true) = default;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s misc-non-copyable-objects %t
+
+typedef struct FILE {} FILE;
+typedef struct pthread_cond_t {} pthread_cond_t;
+typedef int pthread_mutex_t;
+
+// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'? [misc-non-copyable-objects]
+void g(FILE f);
+// CHECK-MESSAGES: :[[@LINE+1]]:24: warning: 'm' declared as type 'pthread_mutex_t', which is unsafe to copy; did you mean 'pthread_mutex_t *'?
+void h(pthread_mutex_t m);
+// CHECK-MESSAGES: :[[@LINE+1]]:23: warning: 'c' declared as type 'pthread_cond_t', which is unsafe to copy; did you mean 'pthread_cond_t *'?
+void i(pthread_cond_t c);
+
+struct S {
+ pthread_cond_t c; // ok
+ // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ FILE f;
+};
+
+void func(FILE *f) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f1' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ FILE f1; // match
+ // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: 'f2' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+ FILE f2 = *f;
+ // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: 'f3' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ struct FILE f3;
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+ (void)sizeof(*f);
+ (void)sizeof(FILE);
+ // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+ g(*f);
+
+ pthread_mutex_t m; // ok
+ h(m); // ok
+
+ pthread_cond_t c; // ok
+ i(c); // ok
+
+ pthread_mutex_t *m1 = &m; // ok
+ // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: expression has opaque data structure type 'pthread_mutex_t'; type should only be used as a pointer and not dereferenced
+ h(*m1);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-non-copyable-objects %t
+
+namespace std {
+typedef struct FILE {} FILE;
+}
+using namespace std;
+
+// CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'? [misc-non-copyable-objects]
+void g(std::FILE f);
+
+struct S {
+ // CHECK-MESSAGES: :[[@LINE+1]]:10: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ ::FILE f;
+};
+
+void func(FILE *f) {
+ // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: 'f1' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ std::FILE f1; // match
+ // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: 'f2' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+ ::FILE f2 = *f; // match, match
+ // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: 'f3' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+ struct FILE f3; // match
+ // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+ (void)sizeof(*f); // match
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-redundant-expression %t -- -- -std=c++11
+
+typedef __INT64_TYPE__ I64;
+
+struct Point {
+ int x;
+ int y;
+ int a[5];
+} P;
+
+extern Point P1;
+extern Point P2;
+
+extern int foo(int x);
+extern int bar(int x);
+extern int bat(int x, int y);
+
+int Test(int X, int Y) {
+ if (X - X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent [misc-redundant-expression]
+ if (X / X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X % X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+ if (X & X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X | X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X ^ X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+ if (X < X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X <= X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X > X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X >= X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+ if (X && X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+ if (X || X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+ if (X != (((X)))) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+ if (X + 1 == X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+ if (X + 1 != X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+ if (X + 1 <= X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+ if (X + 1 >= X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+
+ if ((X != 1 || Y != 1) && (X != 1 || Y != 1)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of operator are equivalent
+ if (P.a[X - P.x] != P.a[X - P.x]) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both side of operator are equivalent
+
+ if ((int)X < (int)X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+
+ if ( + "dummy" == + "dummy") return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+ if (L"abc" == L"abc") return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+
+ if (foo(0) - 2 < foo(0) - 2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+ if (foo(bar(0)) < (foo(bar((0))))) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+
+ if (P1.x < P2.x && P1.x < P2.x) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+ if (P2.a[P1.x + 2] < P2.x && P2.a[(P1.x) + (2)] < (P2.x)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: both side of operator are equivalent
+
+ return 0;
+}
+
+int Valid(int X, int Y) {
+ if (X != Y) return 1;
+ if (X == Y + 0) return 1;
+ if (P.x == P.y) return 1;
+ if (P.a[P.x] < P.a[P.y]) return 1;
+ if (P.a[0] < P.a[1]) return 1;
+
+ if (P.a[0] < P.a[0ULL]) return 1;
+ if (0 < 0ULL) return 1;
+ if ((int)0 < (int)0ULL) return 1;
+
+ if (++X != ++X) return 1;
+ if (P.a[X]++ != P.a[X]++) return 1;
+ if (P.a[X++] != P.a[X++]) return 1;
+
+ if ("abc" == "ABC") return 1;
+ if (foo(bar(0)) < (foo(bat(0, 1)))) return 1;
+ return 0;
+}
+
+int TestConditional(int x, int y) {
+ int k = 0;
+ k += (y < 0) ? x : x;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'true' and 'false' expression are equivalent
+ k += (y < 0) ? x + 1 : x + 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'true' and 'false' expression are equivalent
+ return k;
+}
+
+struct MyStruct {
+ int x;
+} Q;
+bool operator==(const MyStruct& lhs, const MyStruct& rhs) { return lhs.x == rhs.x; }
+bool TestOperator(const MyStruct& S) {
+ if (S == Q) return false;
+ return S == S;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: both side of overloaded operator are equivalent
+}
+
+#define LT(x, y) (void)((x) < (y))
+#define COND(x, y, z) ((x)?(y):(z))
+#define EQUALS(x, y) (x) == (y)
+
+int TestMacro(int X, int Y) {
+ LT(0, 0);
+ LT(1, 0);
+ LT(X, X);
+ LT(X+1, X + 1);
+ COND(X < Y, X, X);
+ EQUALS(Q, Q);
+}
+
+int TestFalsePositive(int* A, int X, float F) {
+ // Produced by bison.
+ X = A[(2) - (2)];
+ X = A['a' - 'a'];
+
+ // Testing NaN.
+ if (F != F && F == F) return 1;
+ return 0;
+}
+
+int TestBannedMacros() {
+#define EAGAIN 3
+#define NOT_EAGAIN 3
+ if (EAGAIN == 0 | EAGAIN == 0) return 0;
+ if (NOT_EAGAIN == 0 | NOT_EAGAIN == 0) return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both side of operator are equivalent
+}
+
+struct MyClass {
+static const int Value = 42;
+};
+template <typename T, typename U>
+void TemplateCheck() {
+ static_assert(T::Value == U::Value, "should be identical");
+ static_assert(T::Value == T::Value, "should be identical");
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of overloaded operator are equivalent
+}
+void TestTemplate() { TemplateCheck<MyClass, MyClass>(); }
+
+int TestArithmetic(int X, int Y) {
+ if (X + 1 == X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X + 1 != X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+ if (X - 1 == X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X - 1 != X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ if (X + 1LL == X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X + 1ULL == X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+
+ if (X == X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+ if (X != X + 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+ if (X == X - 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+ if (X != X - 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+
+ if (X != X - 1U) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+ if (X != X - 1LL) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+
+ if ((X+X) != (X+X) - 1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ if (X + 1 == X + 2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X + 1 != X + 2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ if (X - 1 == X - 2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X - 1 != X - 2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ if (X + 1 == X - -1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+ if (X + 1 != X - -1) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X + 1 == X - -2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X + 1 != X - -2) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ if (X + 1 == X - (~0)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+ if (X + 1 == X - (~0U)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+ if (X + 1 == X - (~0ULL)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+ // Should not match.
+ if (X + 0.5 == X) return 1;
+ if (X + 1 == Y) return 1;
+ if (X + 1 == Y + 1) return 1;
+ if (X + 1 == Y + 2) return 1;
+
+ return 0;
+}
+
+int TestBitwise(int X) {
+ if ((X & 0xFF) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if ((X & 0xFF) != 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+ if ((X | 0xFF) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if ((X | 0xFF) != 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+ if ((X | 0xFFULL) != 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always true
+ if ((X | 0xFF) != 0xF00ULL) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+ if ((0xFF & X) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if ((0xFF & X) != 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+ if ((0xFF & X) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if ((0xFF & X) != 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+ if ((0xFFLL & X) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false
+ if ((0xFF & X) == 0xF00ULL) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+
+ return 0;
+}
+
+int TestRelational(int X, int Y) {
+ if (X == 10 && X != 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X == 10 && (X != 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X == 10 && !(X == 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (!(X != 10) && !(X == 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+
+ if (X == 10ULL && X != 10ULL) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if (!(X != 10U) && !(X == 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+ if (!(X != 10LL) && !(X == 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false
+ if (!(X != 10ULL) && !(X == 10)) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always false
+
+ if (X == 0 && X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+ if (X != 0 && !X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+ if (X && !X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+
+ if (X && !!X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: equivalent expression on both side of logical operator
+ if (X != 0 && X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+ if (X != 0 && !!X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+ if (X == 0 && !X) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+
+ if (X == 10 && X > 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X == 10 && X < 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X < 10 && X > 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+ if (X <= 10 && X > 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+ if (X < 10 && X >= 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+ if (X < 10 && X == 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+
+ if (X > 5 && X <= 5) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+ if (X > -5 && X <= -5) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+
+ if (X < 10 || X >= 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
+ if (X <= 10 || X > 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
+ if (X <= 10 || X >= 11) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
+ if (X != 7 || X != 14) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
+
+ if (X < 7 && X < 6) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X < 7 && X < 7) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+ if (X < 7 && X < 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+
+ if (X < 7 && X <= 5) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X < 7 && X <= 6) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: equivalent expression on both side of logical operator
+ if (X < 7 && X <= 7) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+ if (X < 7 && X <= 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+
+ if (X <= 7 && X < 6) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X <= 7 && X < 7) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X <= 7 && X < 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+
+ if (X <= 7 && X <= 5) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X <= 7 && X <= 6) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X <= 7 && X <= 7) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+ if (X <= 7 && X <= 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: expression is redundant
+
+ if (X == 11 && X > 10) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant
+ if (X == 11 && X < 12) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant
+ if (X > 10 && X == 11) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X < 12 && X == 11) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+ if (X != 11 && X == 42) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X != 11 && X > 11) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X != 11 && X < 11) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X != 11 && X < 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+ if (X != 11 && X > 14) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+ if (X < 7 || X < 6) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+ if (X < 7 || X < 7) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+ if (X < 7 || X < 8) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+ // Should not match.
+ if (X == 10 && Y == 10) return 1;
+ if (X != 10 && X != 12) return 1;
+ if (X == 10 || X == 12) return 1;
+ if (X < 10 || X > 12) return 1;
+ if (X > 10 && X < 12) return 1;
+ if (X < 10 || X >= 12) return 1;
+ if (X > 10 && X <= 12) return 1;
+ if (X <= 10 || X > 12) return 1;
+ if (X >= 10 && X < 12) return 1;
+ if (X <= 10 || X >= 12) return 1;
+ if (X >= 10 && X <= 12) return 1;
+ if (X >= 10 && X <= 11) return 1;
+ if (X >= 10 && X < 11) return 1;
+ if (X > 10 && X <= 11) return 1;
+ if (X > 10 && X != 11) return 1;
+ if (X >= 10 && X <= 10) return 1;
+ if (X <= 10 && X >= 10) return 1;
+ if (!X && !Y) return 1;
+ if (!X && Y) return 1;
+ if (!X && Y == 0) return 1;
+ if (X == 10 && Y != 10) return 1;
+ if (X < 0 || X > 0) return 1;
+
+ return 0;
+}
+
+int TestValidExpression(int X) {
+ if (X - 1 == 1 - X) return 1;
+ if (2 * X == X) return 1;
+ if ((X << 1) == X) return 1;
+
+ return 0;
+}
+
+enum Color { Red, Yellow, Green };
+int TestRelatiopnalWithEnum(enum Color C) {
+ if (C == Red && C == Yellow) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+ if (C == Red && C != Red) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+ if (C != Red || C != Yellow) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always true
+
+ // Should not match.
+ if (C == Red || C == Yellow) return 1;
+ if (C != Red && C != Yellow) return 1;
+
+ return 0;
+}
+
+template<class T>
+int TestRelationalTemplated(int X) {
+ // This test causes a corner case with |isIntegerConstantExpr| where the type
+ // is dependant. There is an assert failing when evaluating
+ // sizeof(<incomplet-type>).
+ if (sizeof(T) == 4 || sizeof(T) == 8) return 1;
+
+ if (X + 0 == -X) return 1;
+ if (X + 0 < X) return 1;
+
+ return 0;
+}
+
+int TestWithSignedUnsigned(int X) {
+ if (X + 1 == X + 1ULL) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+ if ((X & 0xFFU) == 0xF00) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+ if ((X & 0xFF) == 0xF00U) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+ if ((X & 0xFFU) == 0xF00U) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+
+ return 0;
+}
+
+int TestWithLong(int X, I64 Y) {
+ if (X + 0ULL == -X) return 1;
+ if (Y + 0 == -Y) return 1;
+ if (Y <= 10 && X >= 10LL) return 1;
+ if (Y <= 10 && X >= 10ULL) return 1;
+ if (X <= 10 || X > 12LL) return 1;
+ if (X <= 10 || X > 12ULL) return 1;
+ if (Y <= 10 || Y > 12) return 1;
+
+ return 0;
+}
+
+int TestWithMinMaxInt(int X) {
+ if (X <= X + 0xFFFFFFFFU) return 1;
+ if (X <= X + 0x7FFFFFFF) return 1;
+ if (X <= X + 0x80000000) return 1;
+ if (X <= 0xFFFFFFFFU && X > 0) return 1;
+ if (X <= 0xFFFFFFFFU && X > 0U) return 1;
+
+ if (X + 0x80000000 == X - 0x80000000) return 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always true
+
+ if (X > 0x7FFFFFFF || X < ((-0x7FFFFFFF)-1)) return 1;
+ if (X <= 0x7FFFFFFF && X >= ((-0x7FFFFFFF)-1)) return 1;
+
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-sizeof-container %t -- -- -std=c++11 -target x86_64-unknown-unknown
+
+namespace std {
+
+typedef unsigned int size_t;
+
+template <typename T>
+struct basic_string {
+ size_t size() const;
+};
+
+template <typename T>
+basic_string<T> operator+(const basic_string<T> &, const T *);
+
+typedef basic_string<char> string;
+
+template <typename T>
+struct vector {
+ size_t size() const;
+};
+
+// std::bitset<> is not a container. sizeof() is reasonable for it.
+template <size_t N>
+struct bitset {
+ size_t size() const;
+};
+
+// std::array<> is, well, an array. sizeof() is reasonable for it.
+template <typename T, size_t N>
+struct array {
+ size_t size() const;
+};
+
+class fake_container1 {
+ size_t size() const; // non-public
+};
+
+struct fake_container2 {
+ size_t size(); // non-const
+};
+
+}
+
+using std::size_t;
+
+#define ARRAYSIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+#define ARRAYSIZE2(a) \
+ (((sizeof(a)) / (sizeof(*(a)))) / static_cast<size_t>(!((sizeof(a)) % (sizeof(*(a))))))
+
+struct string {
+ std::size_t size() const;
+};
+
+template<typename T>
+void g(T t) {
+ (void)sizeof(t);
+}
+
+void f() {
+ string s1;
+ std::string s2;
+ std::vector<int> v;
+
+ int a = 42 + sizeof(s1);
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: sizeof() doesn't return the size of the container; did you mean .size()? [misc-sizeof-container]
+ a = 123 * sizeof(s2);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: sizeof() doesn't return the size
+ a = 45 + sizeof(s2 + "asdf");
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: sizeof() doesn't return the size
+ a = sizeof(v);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: sizeof() doesn't return the size
+ a = sizeof(std::vector<int>{});
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: sizeof() doesn't return the size
+
+ a = sizeof(a);
+ a = sizeof(int);
+ a = sizeof(std::string);
+ a = sizeof(std::vector<int>);
+
+ g(s1);
+ g(s2);
+ g(v);
+
+ std::fake_container1 fake1;
+ std::fake_container2 fake2;
+ std::bitset<7> std_bitset;
+ std::array<int, 3> std_array;
+
+ a = sizeof(fake1);
+ a = sizeof(fake2);
+ a = sizeof(std_bitset);
+ a = sizeof(std_array);
+
+
+ std::string arr[3];
+ a = ARRAYSIZE(arr);
+ a = ARRAYSIZE2(arr);
+ a = sizeof(arr) / sizeof(arr[0]);
+
+ (void)a;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-sizeof-expression %t
+
+class C {
+ int size() { return sizeof(this); }
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(this)'
+};
+
+#define LEN 8
+
+int X;
+extern int A[10];
+extern short B[10];
+
+#pragma pack(1)
+struct S { char a, b, c; };
+
+int Test1(const char* ptr) {
+ int sum = 0;
+ sum += sizeof(LEN);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
+ sum += sizeof(LEN + 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
+ sum += sizeof(sum, LEN);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(..., ...)'
+ sum += sizeof(sizeof(X));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(LEN + sizeof(X));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(LEN + LEN + sizeof(X));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(LEN + (LEN + sizeof(X)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(LEN + -sizeof(X));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(LEN + - + -sizeof(X));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+ sum += sizeof(char) / sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+ sum += sizeof(A) / sizeof(S);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+ sum += sizeof(char) / sizeof(int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+ sum += sizeof(char) / sizeof(A);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+ sum += sizeof(B[0]) / sizeof(A);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+ sum += sizeof(ptr) / sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+ sum += sizeof(ptr) / sizeof(ptr[0]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+ sum += sizeof(ptr) / sizeof(char*);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+ sum += sizeof(ptr) / sizeof(void*);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+ sum += sizeof(ptr) / sizeof(const void volatile*);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+ sum += sizeof(ptr) / sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+ sum += sizeof(ptr) / sizeof(ptr[0]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+ sum += sizeof(int) * sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+ sum += sizeof(ptr) * sizeof(ptr[0]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+ sum += sizeof(int) * (2 * sizeof(char));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+ sum += (2 * sizeof(char)) * sizeof(int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+ if (sizeof(A) < 0x100000) sum += 42;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant
+ if (sizeof(A) <= 0xFFFFFFFEU) sum += 42;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant
+ return sum;
+}
+
+typedef char MyChar;
+typedef const MyChar MyConstChar;
+
+int CE0 = sizeof sizeof(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE1 = sizeof +sizeof(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE2 = sizeof sizeof(const char*);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE3 = sizeof sizeof(const volatile char* const*);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE4 = sizeof sizeof(MyConstChar);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+
+int Test2(MyConstChar* A) {
+ int sum = 0;
+ sum += sizeof(MyConstChar) / sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+ sum += sizeof(MyConstChar) / sizeof(MyChar);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+ sum += sizeof(A[0]) / sizeof(char);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+ return sum;
+}
+
+template <int T>
+int Foo() { int A[T]; return sizeof(T); }
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)'
+template <typename T>
+int Bar() { T A[5]; return sizeof(A[0]) / sizeof(T); }
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+int Test3() { return Foo<42>() + Bar<char>(); }
+
+static const char* kABC = "abc";
+static const wchar_t* kDEF = L"def";
+int Test4(const char A[10]) {
+ int sum = 0;
+ sum += sizeof(kABC);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(char*)'
+ sum += sizeof(kDEF);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(char*)'
+ return sum;
+}
+
+int Test5() {
+ typedef int Array10[10];
+
+ struct MyStruct {
+ Array10 arr;
+ Array10* ptr;
+ };
+ typedef const MyStruct TMyStruct;
+
+ static TMyStruct kGlocalMyStruct = {};
+ static TMyStruct volatile * kGlocalMyStructPtr = &kGlocalMyStruct;
+
+ MyStruct S;
+ Array10 A10;
+
+ int sum = 0;
+ sum += sizeof(&S.arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(&kGlocalMyStruct.arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(&kGlocalMyStructPtr->arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(S.arr + 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(+ S.arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof((int*)S.arr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+ sum += sizeof(S.ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(kGlocalMyStruct.ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(kGlocalMyStructPtr->ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+ sum += sizeof(&kGlocalMyStruct);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(&S);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+ sum += sizeof(&A10);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+ return sum;
+}
+
+int ValidExpressions() {
+ int A[] = {1, 2, 3, 4};
+ static const char str[] = "hello";
+ static const char* ptr[] { "aaa", "bbb", "ccc" };
+ int sum = 0;
+ if (sizeof(A) < 10)
+ sum += sizeof(A);
+ sum += sizeof(int);
+ sum += sizeof(A[sizeof(A) / sizeof(int)]);
+ sum += sizeof(&A[sizeof(A) / sizeof(int)]);
+ sum += sizeof(sizeof(0)); // Special case: sizeof size_t.
+ sum += sizeof(void*);
+ sum += sizeof(void const *);
+ sum += sizeof(void const *) / 4;
+ sum += sizeof(str);
+ sum += sizeof(str) / sizeof(char);
+ sum += sizeof(str) / sizeof(str[0]);
+ sum += sizeof(ptr) / sizeof(ptr[0]);
+ sum += sizeof(ptr) / sizeof(*(ptr));
+ return sum;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-static-assert %t -- -- -std=c11
+// RUN: clang-tidy %s -checks=-*,misc-static-assert -- -std=c99 | count 0
+
+void abort() {}
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x) \
+ if (!(x)) \
+ abort()
+#endif
+
+void f(void) {
+ int x = 1;
+ assert(x == 0);
+ // CHECK-FIXES: {{^ }}assert(x == 0);
+
+ #define static_assert(x, msg) _Static_assert(x, msg)
+ assert(11 == 5 + 6);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(11 == 5 + 6, "");
+ #undef static_assert
+
+ assert(10 == 5 + 5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(10 == 5 + 5, "");
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-static-assert %t
+
+void abort() {}
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x) \
+ if (!(x)) \
+ abort()
+#endif
+
+void print(...);
+
+#define ZERO_MACRO 0
+
+#define False false
+#define FALSE 0
+
+#define my_macro() assert(0 == 1)
+// CHECK-FIXES: #define my_macro() assert(0 == 1)
+
+constexpr bool myfunc(int a, int b) { return a * b == 0; }
+
+typedef __SIZE_TYPE__ size_t;
+extern "C" size_t strlen(const char *s);
+
+class A {
+public:
+ bool method() { return true; }
+};
+
+class B {
+public:
+ constexpr bool method() { return true; }
+};
+
+template <class T> void doSomething(T t) {
+ assert(myfunc(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be replaced by static_assert() [misc-static-assert]
+ // CHECK-FIXES: {{^ }}static_assert(myfunc(1, 2), "");
+
+ assert(t.method());
+ // CHECK-FIXES: {{^ }}assert(t.method());
+
+ assert(sizeof(T) == 123);
+}
+
+int main() {
+ my_macro();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}my_macro();
+
+ assert(myfunc(1, 2) && (3 == 4));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(myfunc(1, 2) && (3 == 4), "");
+
+ int x = 1;
+ assert(x == 0);
+ // CHECK-FIXES: {{^ }}assert(x == 0);
+
+ A a;
+ B b;
+
+ doSomething<A>(a);
+ doSomething<B>(b);
+
+ assert(false);
+ // CHECK-FIXES: {{^ }}assert(false);
+
+ assert(False);
+ // CHECK-FIXES: {{^ }}assert(False);
+ assert(FALSE);
+ // CHECK-FIXES: {{^ }}assert(FALSE);
+
+ assert(ZERO_MACRO);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(ZERO_MACRO, "");
+
+ assert(!"Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(!"Don't report me!");
+
+ assert(0 && "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(0 && "Don't report me!");
+
+ assert(false && "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(false && "Don't report me!");
+
+#define NULL ((void*)0)
+ assert(NULL && "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(NULL && "Don't report me!");
+
+ assert(NULL == "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(NULL == "Don't report me!");
+
+ assert("Don't report me!" == NULL);
+ // CHECK-FIXES: {{^ }}assert("Don't report me!" == NULL);
+
+ assert(0 == "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(0 == "Don't report me!");
+
+#define NULL ((unsigned int)0)
+ assert(NULL && "Report me!");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(NULL , "Report me!");
+
+#define NULL __null
+ assert(__null == "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(__null == "Don't report me!");
+ assert(NULL == "Don't report me!");
+ // CHECK-FIXES: {{^ }}assert(NULL == "Don't report me!");
+#undef NULL
+
+ assert(ZERO_MACRO && "Report me!");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(ZERO_MACRO , "Report me!");
+
+ assert(0);
+
+#define false false
+ assert(false);
+
+#define false 0
+ assert(false);
+#undef false
+
+ assert(10==5 && "Report me!");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(10==5 , "Report me!");
+
+ assert(strlen("12345") == 5);
+ // CHECK-FIXES: {{^ }}assert(strlen("12345") == 5);
+
+#define assert(e) (__builtin_expect(!(e), 0) ? print (#e, __FILE__, __LINE__) : (void)0)
+ assert(false);
+ // CHECK-FIXES: {{^ }}assert(false);
+
+ assert(10 == 5 + 5);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+ // CHECK-FIXES: {{^ }}static_assert(10 == 5 + 5, "");
+#undef assert
+
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-string-compare %t -- -- -std=c++11
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+class basic_string {
+public:
+ basic_string();
+ basic_string(const C *, unsigned int size);
+ int compare(const basic_string<char> &str) const;
+ int compare(const C *) const;
+ int compare(int, int, const basic_string<char> &str) const;
+ bool empty();
+};
+bool operator==(const basic_string<char> &lhs, const basic_string<char> &rhs);
+bool operator!=(const basic_string<char> &lhs, const basic_string<char> &rhs);
+bool operator==(const basic_string<char> &lhs, const char *&rhs);
+typedef basic_string<char> string;
+}
+
+void func(bool b);
+
+std::string comp() {
+ std::string str("a", 1);
+ return str;
+}
+
+void Test() {
+ std::string str1("a", 1);
+ std::string str2("b", 1);
+
+ if (str1.compare(str2)) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [misc-string-compare]
+ if (!str1.compare(str2)) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:8: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [misc-string-compare]
+ if (str1.compare(str2) == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str1 == str2) {
+ if (str1.compare(str2) != 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str1 != str2) {
+ if (str1.compare("foo") == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str1 == "foo") {
+ if (0 == str1.compare(str2)) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str2 == str1) {
+ if (0 != str1.compare(str2)) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str2 != str1) {
+ func(str1.compare(str2));
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: do not use 'compare' to test equality of strings;
+ if (str2.empty() || str1.compare(str2) != 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:23: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str2.empty() || str1 != str2) {
+ std::string *str3 = &str1;
+ if (str3->compare(str2)) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ if (str3->compare(str2) == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (*str3 == str2) {
+ if (str2.compare(*str3) == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str2 == *str3) {
+ if (comp().compare(str1) == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (comp() == str1) {
+ if (str1.compare(comp()) == 0) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+ // CHECK-FIXES: if (str1 == comp()) {
+ if (str1.compare(comp())) {
+ }
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+}
+
+void Valid() {
+ std::string str1("a", 1);
+ std::string str2("b", 1);
+ if (str1 == str2) {
+ }
+ if (str1 != str2) {
+ }
+ if (str1.compare(str2) == str1.compare(str2)) {
+ }
+ if (0 == 0) {
+ }
+ if (str1.compare(str2) > 0) {
+ }
+ if (str1.compare(1, 3, str2)) {
+ }
+ if (str1.compare(str2) > 0) {
+ }
+ if (str1.compare(str2) < 0) {
+ }
+ if (str1.compare(str2) == 2) {
+ }
+ if (str1.compare(str2) == -3) {
+ }
+ if (str1.compare(str2) == 1) {
+ }
+ if (str1.compare(str2) == -1) {
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-string-constructor %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C> >
+struct basic_string {
+ basic_string();
+ basic_string(const C*, unsigned int size);
+ basic_string(unsigned int size, C c);
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+const char* kText = "";
+const char kText2[] = "";
+extern const char kText3[];
+
+void Test() {
+ std::string str('x', 4);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: string constructor parameters are probably swapped; expecting string(count, character) [misc-string-constructor]
+ // CHECK-FIXES: std::string str(4, 'x');
+ std::wstring wstr(L'x', 4);
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: string constructor parameters are probably swapped
+ // CHECK-FIXES: std::wstring wstr(4, L'x');
+ std::string s0(0, 'x');
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+ std::string s1(-4, 'x');
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
+ std::string s2(0x1000000, 'x');
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+
+ std::string q0("test", 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+ std::string q1(kText, -4);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
+ std::string q2("test", 200);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+ std::string q3(kText, 200);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+ std::string q4(kText2, 200);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+ std::string q5(kText3, 0x1000000);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+}
+
+void Valid() {
+ std::string empty();
+ std::string str(4, 'x');
+ std::wstring wstr(4, L'x');
+ std::string s1("test", 4);
+ std::string s2("test", 3);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-string-integer-assignment %t
+
+namespace std {
+template<typename T>
+struct basic_string {
+ basic_string& operator=(T);
+ basic_string& operator=(basic_string);
+ basic_string& operator+=(T);
+ basic_string& operator+=(basic_string);
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+typedef int MyArcaneChar;
+
+int main() {
+ std::string s;
+ std::wstring ws;
+ int x = 5;
+
+ s = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a character code when assigning {{.*}} [misc-string-integer-assignment]
+// CHECK-FIXES: {{^}} s = '6';{{$}}
+ s = 66;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} s = "66";{{$}}
+ s = x;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} s = std::to_string(x);{{$}}
+ s = 'c';
+ s = static_cast<char>(6);
+
+// +=
+ ws += 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} ws += L'6';{{$}}
+ ws += 66;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} ws += L"66";{{$}}
+ ws += x;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} ws += std::to_wstring(x);{{$}}
+ ws += L'c';
+ ws += (wchar_t)6;
+
+ std::basic_string<MyArcaneChar> as;
+ as = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}} as = 6;{{$}}
+
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-string-literal-with-embedded-nul %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ typedef basic_string<C, T, A> _Type;
+ basic_string();
+ basic_string(const C *p, const A &a = A());
+
+ _Type& operator+=(const C* s);
+ _Type& operator=(const C* s);
+};
+
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>> wstring;
+}
+
+bool operator==(const std::string&, const char*);
+bool operator==(const char*, const std::string&);
+
+
+const char Valid[] = "This is valid \x12.";
+const char Strange[] = "This is strange \0x12 and must be fixed";
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious embedded NUL character [misc-string-literal-with-embedded-nul]
+
+const char textA[] = "\0x01\0x02\0x03\0x04";
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious embedded NUL character
+const wchar_t textW[] = L"\0x01\0x02\0x03\0x04";
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious embedded NUL character
+
+const char A[] = "\0";
+const char B[] = "\0x";
+const char C[] = "\0x1";
+const char D[] = "\0x11";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character
+
+const wchar_t E[] = L"\0";
+const wchar_t F[] = L"\0x";
+const wchar_t G[] = L"\0x1";
+const wchar_t H[] = L"\0x11";
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious embedded NUL character
+
+const char I[] = "\000\000\000\000";
+const char J[] = "\0\0\0\0\0\0";
+const char K[] = "";
+
+const char L[] = "\0x12" "\0x12" "\0x12" "\0x12";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character
+
+void TestA() {
+ std::string str1 = "abc\0def";
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal
+ std::string str2 = "\0";
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal
+ std::string str3("\0");
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal
+ std::string str4{"\x00\x01\x02\x03"};
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal
+
+ std::string str;
+ str += "abc\0def";
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: truncated string literal
+ str = "abc\0def";
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: truncated string literal
+
+ if (str == "abc\0def") return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: truncated string literal
+ if ("abc\0def" == str) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: truncated string literal
+}
+
+void TestW() {
+ std::wstring str1 = L"abc\0def";
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal
+ std::wstring str2 = L"\0";
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal
+ std::wstring str3(L"\0");
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal
+ std::wstring str4{L"\x00\x01\x02\x03"};
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 1}]}" --
+
+enum A {
+ A = 1,
+ B = 2,
+ C = 4,
+ D = 8,
+ E = 16,
+ F = 32,
+ G = 63
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2
+// CHECK-MESSAGES: :76:7: note: used here as a bitmask
+enum X {
+ X = 8,
+ Y = 16,
+ Z = 4,
+ ZZ = 3
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [misc-suspicious-enum-usage]
+// CHECK-MESSAGES: :70:13: note: used here as a bitmask
+};
+// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2
+// CHECK-MESSAGES: :73:8: note: used here as a bitmask
+enum PP {
+ P = 2,
+ Q = 3,
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2
+ // CHECK-MESSAGES: :65:11: note: used here as a bitmask
+ R = 4,
+ S = 8,
+ T = 16,
+ U = 31
+};
+
+enum {
+ H,
+ I,
+ J,
+ K,
+ L
+};
+
+enum Days {
+ Monday,
+ Tuesday,
+ Wednesday,
+ Thursday,
+ Friday,
+ Saturday,
+ Sunday
+};
+
+Days bestDay() {
+ return Friday;
+}
+
+int trigger() {
+ if (bestDay() | A)
+ return 1;
+ // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types
+ if (I | Y)
+ return 1;
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types
+ if (P + Q == R)
+ return 1;
+ else if ((S | R) == T)
+ return 1;
+ else
+ int k = ZZ | Z;
+ unsigned p = R;
+ PP pp = Q;
+ p |= pp;
+
+ enum X x = Z;
+ p = x | Z;
+ return 0;
+}
+
+int dont_trigger() {
+ int a = 1, b = 5;
+ int c = a + b;
+ int d = c | H, e = b * a;
+ a = B | C;
+ b = X | Z;
+
+ unsigned bitflag;
+ enum A aa = B;
+ bitflag = aa | C;
+
+ if (Tuesday != Monday + 1 ||
+ Friday - Thursday != 1 ||
+ Sunday + Wednesday == (Sunday | Wednesday))
+ return 1;
+ if (H + I + L == 42)
+ return 1;
+ return 42;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 0}]}" --
+
+enum Empty {
+};
+
+enum A {
+ A = 1,
+ B = 2,
+ C = 4,
+ D = 8,
+ E = 16,
+ F = 32,
+ G = 63
+};
+
+enum X {
+ X = 8,
+ Y = 16,
+ Z = 4
+};
+
+enum {
+ P = 2,
+ Q = 3,
+ R = 4,
+ S = 8,
+ T = 16
+};
+
+enum {
+ H,
+ I,
+ J,
+ K,
+ L
+};
+
+enum Days {
+ Monday,
+ Tuesday,
+ Wednesday,
+ Thursday,
+ Friday,
+ Saturday,
+ Sunday
+};
+
+Days bestDay() {
+ return Friday;
+}
+
+int trigger() {
+ Empty EmptyVal;
+ int emptytest = EmptyVal | B;
+ if (bestDay() | A)
+ return 1;
+ // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types
+ if (I | Y)
+ return 1;
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types
+}
+
+int dont_trigger() {
+ unsigned p;
+ p = Q | P;
+
+ if (A + G == E)
+ return 1;
+ else if ((Q | R) == T)
+ return 1;
+ else
+ int k = T | Q;
+
+ Empty EmptyVal;
+ int emptytest = EmptyVal | B;
+
+ int a = 1, b = 5;
+ int c = a + b;
+ int d = c | H, e = b * a;
+ a = B | C;
+ b = X | Z;
+
+ if (Tuesday != Monday + 1 ||
+ Friday - Thursday != 1 ||
+ Sunday + Wednesday == (Sunday | Wednesday))
+ return 1;
+ if (H + I + L == 42)
+ return 1;
+ return 42;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-missing-comma %t
+
+const char* Cartoons[] = {
+ "Bugs Bunny",
+ "Homer Simpson",
+ "Mickey Mouse",
+ "Bart Simpson",
+ "Charlie Brown" // There is a missing comma here.
+ "Fred Flintstone",
+ "Popeye",
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma]
+
+const wchar_t* Colors[] = {
+ L"Red", L"Yellow", L"Blue", L"Green", L"Purple", L"Rose", L"White", L"Black"
+};
+
+// The following array should not trigger any warnings. There is more than 5
+// elements, but they are all concatenated string literals.
+const char* HttpCommands[] = {
+ "GET / HTTP/1.0\r\n"
+ "\r\n",
+
+ "GET /index.html HTTP/1.0\r\n"
+ "\r\n",
+
+ "GET /favicon.ico HTTP/1.0\r\n"
+ "header: dummy"
+ "\r\n",
+
+ "GET /index.html-en HTTP/1.0\r\n"
+ "\r\n",
+
+ "GET /index.html-fr HTTP/1.0\r\n"
+ "\r\n",
+
+ "GET /index.html-es HTTP/1.0\r\n"
+ "\r\n",
+};
+
+// This array is too small to trigger a warning.
+const char* SmallArray[] = {
+ "a" "b", "c"
+};
+
+// Parentheses should be enough to avoid warnings.
+const char* ParentheseArray[] = {
+ ("a" "b"), "c",
+ ("d"
+ "e"
+ "f"),
+ "g", "h", "i", "j", "k", "l"
+};
+
+// Indentation should be enough to avoid warnings.
+const char* CorrectlyIndentedArray[] = {
+ "This is a long message "
+ "which is spanning over multiple lines."
+ "And this should be fine.",
+ "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l"
+};
+
+const char* IncorrectlyIndentedArray[] = {
+ "This is a long message "
+ "which is spanning over multiple lines."
+ "And this should be fine.",
+ "a", "b", "c", "d", "e", "f",
+ "g", "h", "i", "j", "k", "l"
+};
+// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma]
+
+const char* TooManyConcatenatedTokensArray[] = {
+ "Dummy line",
+ "Dummy line",
+ "a" "b" "c" "d" "e" "f",
+ "g" "h" "i" "j" "k" "l",
+ "Dummy line",
+ "Dummy line",
+ "Dummy line",
+ "Dummy line",
+};
--- /dev/null
+// RUN: clang-tidy %s -checks="-*,misc-suspicious-semicolon" -- -DERROR 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-ERROR \
+// RUN: -implicit-check-not="{{warning|error}}:"
+// RUN: clang-tidy %s -checks="-*,misc-suspicious-semicolon,clang-diagnostic*" \
+// RUN: -- -DWERROR -Wno-everything -Werror=unused-variable 2>&1 \
+// RUN: | FileCheck %s -check-prefix=CHECK-WERROR \
+// RUN: -implicit-check-not="{{warning|error}}:"
+
+// Note: This test verifies that, the checker does not emit any warning for
+// files that do not compile.
+
+bool g();
+
+void f() {
+ if (g());
+ // CHECK-WERROR: :[[@LINE-1]]:11: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+#if ERROR
+ int a
+ // CHECK-ERROR: :[[@LINE-1]]:8: error: expected ';' at end of declaration [clang-diagnostic-error]
+#elif WERROR
+ int a;
+ // CHECK-WERROR: :[[@LINE-1]]:7: error: unused variable 'a' [clang-diagnostic-unused-variable]
+#else
+#error "One of ERROR or WERROR should be defined.
+#endif
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-semicolon %t
+
+int x = 5;
+
+void nop();
+
+void correct1()
+{
+ if(x < 5) nop();
+}
+
+void correct2()
+{
+ if(x == 5)
+ nop();
+}
+
+void correct3()
+{
+ if(x > 5)
+ {
+ nop();
+ }
+}
+
+void fail1()
+{
+ if(x > 5); nop();
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+ // CHECK-FIXES: if(x > 5) nop();
+}
+
+void fail2()
+{
+ if(x == 5);
+ nop();
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+ // CHECK-FIXES: if(x == 5){{$}}
+}
+
+void fail3()
+{
+ if(x < 5);
+ {
+ nop();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:11: warning: potentially unintended semicolon
+ // CHECK-FIXES: if(x < 5){{$}}
+}
+
+void correct4()
+{
+ while(x % 5 == 1);
+ nop();
+}
+
+void correct5()
+{
+ for(int i = 0; i < x; ++i)
+ ;
+}
+
+void fail4()
+{
+ for(int i = 0; i < x; ++i);
+ nop();
+ // CHECK-MESSAGES: :[[@LINE-2]]:28: warning: potentially unintended semicolon
+ // CHECK-FIXES: for(int i = 0; i < x; ++i){{$}}
+}
+
+void fail5()
+{
+ if(x % 5 == 1);
+ nop();
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: potentially unintended semicolon
+ // CHECK-FIXES: if(x % 5 == 1){{$}}
+}
+
+void fail6() {
+ int a = 0;
+ if (a != 0) {
+ } else if (a != 1);
+ a = 2;
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: potentially unintended semicolon
+ // CHECK-FIXES: } else if (a != 1){{$}}
+}
+
+void fail7() {
+ if (true)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: potentially unintended semicolon
+}
+
+void correct6()
+{
+ do; while(false);
+}
+
+int correct7()
+{
+ int t_num = 0;
+ char c = 'b';
+ char *s = "a";
+ if (s == "(" || s != "'" || c == '"') {
+ t_num += 3;
+ return (c == ')' && c == '\'');
+ }
+
+ return 0;
+}
+
+void correct8() {
+ if (true)
+ ;
+ else {
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \
+// RUN: {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \
+// RUN: -- -std=c99
+
+static const char A[] = "abc";
+
+int strcmp(const char *, const char *);
+
+int test_warning_patterns() {
+ if (strcmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+ // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+ if (strcmp(A, "a") != 0 ||
+ strcmp(A, "b"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: strcmp(A, "b") != 0)
+
+ if (strcmp(A, "a") == 1)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") == -1)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") < '0')
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") < 0.)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+
+ if (!strcmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:8: warning: function 'strcmp' is compared using logical not operator
+ // CHECK-FIXES: if (strcmp(A, "a") == 0)
+}
+
+void test_structure_patterns() {
+ if (strcmp(A, "a")) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: if (strcmp(A, "a") != 0) {}
+
+ while (strcmp(A, "a")) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: while (strcmp(A, "a") != 0) {}
+
+ for (;strcmp(A, "a");) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: for (;strcmp(A, "a") != 0;) {}
+}
+
+int test_valid_patterns() {
+ // The following cases are valid.
+ if (strcmp(A, "a") < 0) return 0;
+ if (strcmp(A, "a") == 0) return 0;
+ if (strcmp(A, "a") <= 0) return 0;
+ if (strcmp(A, "a") == strcmp(A, "b")) return 0;
+ return 1;
+}
+
+int wrapper(const char* a, const char* b) {
+ return strcmp(a, b);
+}
+
+int assignment_wrapper(const char* a, const char* b) {
+ int cmp = strcmp(a, b);
+ return cmp;
+}
+
+int condexpr_wrapper(const char* a, const char* b) {
+ return (a < b) ? strcmp(a, b) : strcmp(b, a);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \
+// RUN: {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \
+// RUN: --
+
+typedef __SIZE_TYPE__ size;
+
+struct locale_t {
+ void* dummy;
+} locale;
+
+static const char A[] = "abc";
+static const unsigned char U[] = "abc";
+static const unsigned char V[] = "xyz";
+static const wchar_t W[] = L"abc";
+
+int strlen(const char *);
+
+int memcmp(const void *, const void *, size);
+int wmemcmp(const wchar_t *, const wchar_t *, size);
+int memicmp(const void *, const void *, size);
+int _memicmp(const void *, const void *, size);
+int _memicmp_l(const void *, const void *, size, locale_t);
+
+int strcmp(const char *, const char *);
+int strncmp(const char *, const char *, size);
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size);
+int stricmp(const char *, const char *);
+int strcmpi(const char *, const char *);
+int strnicmp(const char *, const char *, size);
+int _stricmp(const char *, const char * );
+int _strnicmp(const char *, const char *, size);
+int _stricmp_l(const char *, const char *, locale_t);
+int _strnicmp_l(const char *, const char *, size, locale_t);
+
+int wcscmp(const wchar_t *, const wchar_t *);
+int wcsncmp(const wchar_t *, const wchar_t *, size);
+int wcscasecmp(const wchar_t *, const wchar_t *);
+int wcsicmp(const wchar_t *, const wchar_t *);
+int wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp(const wchar_t *, const wchar_t *);
+int _wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp_l(const wchar_t *, const wchar_t *, locale_t);
+int _wcsnicmp_l(const wchar_t *, const wchar_t *, size, locale_t);
+
+int _mbscmp(const unsigned char *, const unsigned char *);
+int _mbsncmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbcmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbicmp(const unsigned char *, const unsigned char *, size);
+int _mbsicmp(const unsigned char *, const unsigned char *);
+int _mbsnicmp(const unsigned char *, const unsigned char *, size);
+int _mbscmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsncmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsicmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsnicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbcmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+
+int test_warning_patterns() {
+ if (strcmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+ // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+ if (strcmp(A, "a") == 0 ||
+ strcmp(A, "b"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: strcmp(A, "b") != 0)
+
+ if (strcmp(A, "a") == 1)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") == -1)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") == true)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") < '0')
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (strcmp(A, "a") < 0.)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+}
+
+int test_valid_patterns() {
+ // The following cases are valid.
+ if (strcmp(A, "a") < 0)
+ return 0;
+ if (strcmp(A, "a") == 0)
+ return 0;
+ if (strcmp(A, "a") <= 0)
+ return 0;
+
+ if (wcscmp(W, L"a") < 0)
+ return 0;
+ if (wcscmp(W, L"a") == 0)
+ return 0;
+ if (wcscmp(W, L"a") <= 0)
+ return 0;
+
+ return 1;
+}
+
+int test_implicit_compare_with_functions() {
+
+ if (memcmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memcmp' is called without explicitly comparing result
+ // CHECK-FIXES: memcmp(A, "a", 1) != 0)
+
+ if (wmemcmp(W, L"a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wmemcmp' is called without explicitly comparing result
+ // CHECK-FIXES: wmemcmp(W, L"a", 1) != 0)
+
+ if (memicmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memicmp' is called without explicitly comparing result
+ // CHECK-FIXES: memicmp(A, "a", 1) != 0)
+
+ if (_memicmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _memicmp(A, "a", 1) != 0)
+
+ if (_memicmp_l(A, "a", 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _memicmp_l(A, "a", 1, locale) != 0)
+
+ if (strcmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+ // CHECK-FIXES: strcmp(A, "a") != 0)
+
+ if (strncmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncmp' is called without explicitly comparing result
+ // CHECK-FIXES: strncmp(A, "a", 1) != 0)
+
+ if (strcasecmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcasecmp' is called without explicitly comparing result
+ // CHECK-FIXES: strcasecmp(A, "a") != 0)
+
+ if (strncasecmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncasecmp' is called without explicitly comparing result
+ // CHECK-FIXES: strncasecmp(A, "a", 1) != 0)
+
+ if (stricmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'stricmp' is called without explicitly comparing result
+ // CHECK-FIXES: stricmp(A, "a") != 0)
+
+ if (strcmpi(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmpi' is called without explicitly comparing result
+ // CHECK-FIXES: strcmpi(A, "a") != 0)
+
+ if (_stricmp(A, "a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp' is called without explicitly comparing result
+ // CHECK-FIXES: _stricmp(A, "a") != 0)
+
+ if (strnicmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strnicmp' is called without explicitly comparing result
+ // CHECK-FIXES: strnicmp(A, "a", 1) != 0)
+
+ if (_strnicmp(A, "a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _strnicmp(A, "a", 1) != 0)
+
+ if (_stricmp_l(A, "a", locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _stricmp_l(A, "a", locale) != 0)
+
+ if (_strnicmp_l(A, "a", 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _strnicmp_l(A, "a", 1, locale) != 0)
+
+ if (wcscmp(W, L"a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscmp' is called without explicitly comparing result
+ // CHECK-FIXES: wcscmp(W, L"a") != 0)
+
+ if (wcsncmp(W, L"a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsncmp' is called without explicitly comparing result
+ // CHECK-FIXES: wcsncmp(W, L"a", 1) != 0)
+
+ if (wcscasecmp(W, L"a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscasecmp' is called without explicitly comparing result
+ // CHECK-FIXES: wcscasecmp(W, L"a") != 0)
+
+ if (wcsicmp(W, L"a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsicmp' is called without explicitly comparing result
+ // CHECK-FIXES: wcsicmp(W, L"a") != 0)
+
+ if (_wcsicmp(W, L"a"))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _wcsicmp(W, L"a") != 0)
+
+ if (_wcsicmp_l(W, L"a", locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _wcsicmp_l(W, L"a", locale) != 0)
+
+ if (wcsnicmp(W, L"a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsnicmp' is called without explicitly comparing result
+ // CHECK-FIXES: wcsnicmp(W, L"a", 1) != 0)
+
+ if (_wcsnicmp(W, L"a", 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _wcsnicmp(W, L"a", 1) != 0)
+
+ if (_wcsnicmp_l(W, L"a", 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _wcsnicmp_l(W, L"a", 1, locale) != 0)
+
+ if (_mbscmp(U, V))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbscmp(U, V) != 0)
+
+ if (_mbsncmp(U, V, 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsncmp(U, V, 1) != 0)
+
+ if (_mbsnbcmp(U, V, 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnbcmp(U, V, 1) != 0)
+
+ if (_mbsnbicmp(U, V, 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnbicmp(U, V, 1) != 0)
+
+ if (_mbsicmp(U, V))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsicmp(U, V) != 0)
+
+ if (_mbsnicmp(U, V, 1))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnicmp(U, V, 1) != 0)
+
+ if (_mbscmp_l(U, V, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbscmp_l(U, V, locale) != 0)
+
+ if (_mbsncmp_l(U, V, 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsncmp_l(U, V, 1, locale) != 0)
+
+ if (_mbsicmp_l(U, V, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsicmp_l(U, V, locale) != 0)
+
+ if (_mbsnicmp_l(U, V, 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnicmp_l(U, V, 1, locale) != 0)
+
+ if (_mbsnbcmp_l(U, V, 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnbcmp_l(U, V, 1, locale) != 0)
+
+ if (_mbsnbicmp_l(U, V, 1, locale))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp_l' is called without explicitly comparing result
+ // CHECK-FIXES: _mbsnbicmp_l(U, V, 1, locale) != 0)
+
+ return 1;
+}
+
+int strcmp_wrapper1(const char* a, const char* b) {
+ return strcmp(a, b);
+}
+
+int strcmp_wrapper2(const char* a, const char* b) {
+ return (a && b) ? strcmp(a, b) : 0;
+}
+
+#define macro_strncmp(s1, s2, n) \
+ (__extension__ (__builtin_constant_p (n) \
+ && ((__builtin_constant_p (s1) \
+ && strlen (s1) < ((size) (n))) \
+ || (__builtin_constant_p (s2) \
+ && strlen (s2) < ((size) (n)))) \
+ ? strcmp (s1, s2) : strncmp (s1, s2, n)))
+
+int strncmp_macro(const char* a, const char* b) {
+ if (macro_strncmp(a, b, 4))
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+
+ if (macro_strncmp(a, b, 4) == 2)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+ if (macro_strncmp(a, b, 4) <= .0)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+
+ if (macro_strncmp(a, b, 4) + 0)
+ return 0;
+ // CHECK-MESSAGES: [[@LINE-2]]:7: warning: results of function 'strcmp' used by operator '+'
+
+ return 1;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-swapped-arguments %t
+
+void F(int, double);
+
+int SomeFunction();
+
+template <typename T, typename U>
+void G(T a, U b) {
+ F(a, b); // no-warning
+ F(2.0, 4);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(4, 2.0)
+}
+
+void foo() {
+ F(1.0, 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(3, 1.0)
+
+#define M(x, y) x##y()
+
+ double b = 1.0;
+ F(b, M(Some, Function));
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(M(Some, Function), b);
+
+#define N F(b, SomeFunction())
+
+ N;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// In macro, don't emit fixits.
+// CHECK-FIXES: #define N F(b, SomeFunction())
+
+ G(b, 3);
+ G(3, 1.0);
+ G(0, 0);
+
+ F(1.0, 1.0); // no-warning
+ F(3, 1.0); // no-warning
+ F(true, false); // no-warning
+ F(0, 'c'); // no-warning
+
+#define APPLY(f, x, y) f(x, y)
+ APPLY(F, 1.0, 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: APPLY(F, 3, 1.0);
+
+#define PARAMS 1.0, 3
+#define CALL(P) F(P)
+ CALL(PARAMS);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// In macro, don't emit fixits.
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-throw-by-value-catch-by-reference %t -- -- -std=c++11 -fcxx-exceptions
+
+
+class logic_error {
+public:
+ logic_error(const char *message) {}
+};
+
+typedef logic_error *logic_ptr;
+typedef logic_ptr logic_double_typedef;
+
+int lastException;
+
+template <class T> struct remove_reference { typedef T type; };
+template <class T> struct remove_reference<T &> { typedef T type; };
+template <class T> struct remove_reference<T &&> { typedef T type; };
+
+template <typename T> typename remove_reference<T>::type &&move(T &&arg) {
+ return static_cast<typename remove_reference<T>::type &&>(arg);
+}
+
+logic_error CreateException() { return logic_error("created"); }
+
+void testThrowFunc() {
+ throw new logic_error("by pointer");
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+ logic_ptr tmp = new logic_error("by pointer");
+ throw tmp;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+ throw logic_error("by value");
+ auto *literal = "test";
+ throw logic_error(literal);
+ throw "test string literal";
+ throw L"throw wide string literal";
+ const char *characters = 0;
+ throw characters;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+ logic_error lvalue("lvalue");
+ throw lvalue;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+
+ throw move(lvalue);
+ int &ex = lastException;
+ throw ex;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+ throw CreateException();
+}
+
+void throwReferenceFunc(logic_error &ref) { throw ref; }
+
+void catchByPointer() {
+ try {
+ testThrowFunc();
+ } catch (logic_error *e) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+ }
+}
+
+void catchByValue() {
+ try {
+ testThrowFunc();
+ } catch (logic_error e) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches by value; should catch by reference instead [misc-throw-by-value-catch-by-reference]
+ }
+}
+
+void catchByReference() {
+ try {
+ testThrowFunc();
+ } catch (logic_error &e) {
+ }
+}
+
+void catchByConstReference() {
+ try {
+ testThrowFunc();
+ } catch (const logic_error &e) {
+ }
+}
+
+void catchTypedef() {
+ try {
+ testThrowFunc();
+ } catch (logic_ptr) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+ }
+}
+
+void catchAll() {
+ try {
+ testThrowFunc();
+ } catch (...) {
+ }
+}
+
+void catchLiteral() {
+ try {
+ testThrowFunc();
+ } catch (const char *) {
+ } catch (const wchar_t *) {
+ // disabled for now until it is clear
+ // how to enable them in the test
+ //} catch (const char16_t*) {
+ //} catch (const char32_t*) {
+ }
+}
+
+// catching fundamentals should not warn
+void catchFundamental() {
+ try {
+ testThrowFunc();
+ } catch (int) {
+ } catch (double) {
+ } catch (unsigned long) {
+ }
+}
+
+struct TrivialType {
+ double x;
+ double y;
+};
+
+void catchTrivial() {
+ try {
+ testThrowFunc();
+ } catch (TrivialType) {
+ }
+}
+
+typedef logic_error &fine;
+void additionalTests() {
+ try {
+ } catch (int i) { // ok
+ throw i; // ok
+ } catch (fine e) { // ok
+ throw e; // ok
+ } catch (logic_error *e) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+ throw e; // ok, despite throwing a pointer
+ } catch (...) { // ok
+ throw; // ok
+ }
+}
+
+struct S {};
+
+S &returnByReference();
+S returnByValue();
+
+void f() {
+ throw returnByReference(); // Should diagnose
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+ throw returnByValue(); // Should not diagnose
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unconventional-assign-operator %t -- -- -std=c++11 -isystem %S/Inputs/Headers -fno-delayed-template-parsing
+
+namespace std {
+template <typename T>
+struct remove_reference { typedef T type; };
+template <typename T>
+struct remove_reference<T &> { typedef T type; };
+template <typename T>
+struct remove_reference<T &&> { typedef T type; };
+template <typename T>
+typename remove_reference<T>::type &&move(T &&t);
+}
+
+
+struct Good {
+ Good& operator=(const Good&);
+ Good& operator=(Good&&);
+
+ // Assign from other types is fine too.
+ Good& operator=(int);
+};
+
+struct AlsoGood {
+ // By value is also fine.
+ AlsoGood& operator=(AlsoGood);
+};
+
+struct BadReturnType {
+ void operator=(const BadReturnType&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'BadReturnType&' [misc-unconventional-assign-operator]
+ const BadReturnType& operator=(BadReturnType&&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+ void operator=(int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+};
+
+struct BadReturnType2 {
+ BadReturnType2&& operator=(const BadReturnType2&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+ int operator=(BadReturnType2&&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+};
+
+struct BadArgument {
+ BadArgument& operator=(BadArgument&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should take 'BadArgument const&', 'BadArgument&&' or 'BadArgument'
+ BadArgument& operator=(const BadArgument&&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should take 'BadAr
+};
+
+struct BadModifier {
+ BadModifier& operator=(const BadModifier&) const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should not be marked 'const'
+};
+
+struct Deleted {
+ // We don't check the return value of deleted operators.
+ void operator=(const Deleted&) = delete;
+ void operator=(Deleted&&) = delete;
+};
+
+class Private {
+ // We don't check the return value of private operators.
+ // Pre-C++11 way of disabling assignment.
+ void operator=(const Private &);
+};
+
+struct Virtual {
+ virtual Virtual& operator=(const Virtual &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should not be marked 'virtual'
+};
+
+class BadReturnStatement {
+ int n;
+
+public:
+ BadReturnStatement& operator=(BadReturnStatement&& rhs) {
+ n = std::move(rhs.n);
+ return rhs;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: operator=() should always return '*this'
+ }
+
+ // Do not check if return type is different from '&BadReturnStatement'
+ int operator=(int i) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+ n = i;
+ return n;
+ }
+};
+
+namespace pr31531 {
+enum E { e };
+// This declaration makes the 'return *this' below have an unresolved operator
+// in the class template, but not in an instantiation.
+E operator*(E, E);
+
+template <typename>
+struct UnresolvedOperator {
+ UnresolvedOperator &operator=(const UnresolvedOperator &) { return *this; }
+};
+
+UnresolvedOperator<int> UnresolvedOperatorInt;
+
+template <typename>
+struct Template {
+ Template &operator=(const Template &) { return this; }
+ // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: operator=() should always return '*this'
+};
+
+Template<int> TemplateInt;
+}
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,misc-undelegated-constructor -- -std=c++98 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+struct Ctor;
+Ctor foo();
+
+struct Ctor {
+ Ctor();
+ Ctor(int);
+ Ctor(int, int);
+ Ctor(Ctor *i) {
+ Ctor();
+ Ctor(0);
+ Ctor(1, 2);
+ foo();
+ }
+};
+
+Ctor::Ctor() {
+ Ctor(1);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-undelegated-constructor %t
+
+struct Ctor;
+Ctor foo();
+
+struct Ctor {
+ Ctor();
+ Ctor(int);
+ Ctor(int, int);
+ Ctor(Ctor *i) {
+ Ctor();
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor? A temporary object is created here instead [misc-undelegated-constructor]
+ Ctor(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+ Ctor(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+ foo();
+ }
+};
+
+Ctor::Ctor() {
+ Ctor(1);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: did you intend to call a delegated constructor?
+}
+
+Ctor::Ctor(int i) : Ctor(i, 1) {} // properly delegated.
+
+struct Dtor {
+ Dtor();
+ Dtor(int);
+ Dtor(int, int);
+ Dtor(Ctor *i) {
+ Dtor();
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+ Dtor(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+ Dtor(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+ }
+ ~Dtor();
+};
+
+struct Base {};
+struct Derived : public Base {
+ Derived() { Base(); }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: did you intend to call a delegated constructor?
+};
+
+template <typename T>
+struct TDerived : public Base {
+ TDerived() { Base(); }
+};
+
+TDerived<int> t;
--- /dev/null
+// RUN: %check_clang_tidy %s misc-uniqueptr-reset-release %t
+
+namespace std {
+
+template <typename T>
+struct default_delete {};
+
+template <typename T, class Deleter = std::default_delete<T>>
+struct unique_ptr {
+ unique_ptr();
+ explicit unique_ptr(T *);
+ template <typename U, typename E>
+ unique_ptr(unique_ptr<U, E> &&);
+ void reset(T *);
+ T *release();
+};
+} // namespace std
+
+struct Foo {};
+struct Bar : Foo {};
+
+std::unique_ptr<Foo> Create();
+std::unique_ptr<Foo> &Look();
+std::unique_ptr<Foo> *Get();
+
+using FooFunc = void (*)(Foo *);
+using BarFunc = void (*)(Bar *);
+
+void f() {
+ std::unique_ptr<Foo> a, b;
+ std::unique_ptr<Bar> c;
+ std::unique_ptr<Foo> *x = &a;
+ std::unique_ptr<Foo> *y = &b;
+
+ a.reset(b.release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release()) [misc-uniqueptr-reset-release]
+ // CHECK-FIXES: a = std::move(b);
+ a.reset(c.release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr1 = std::move(ptr2)
+ // CHECK-FIXES: a = std::move(c);
+ a.reset(Create().release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release()) [misc-uniqueptr-reset-release]
+ // CHECK-FIXES: a = Create();
+ x->reset(y->release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: prefer ptr1 = std::move(ptr2)
+ // CHECK-FIXES: *x = std::move(*y);
+ Look().reset(Look().release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+ // CHECK-FIXES: Look() = std::move(Look());
+ Get()->reset(Get()->release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+ // CHECK-FIXES: *Get() = std::move(*Get());
+
+ std::unique_ptr<Bar, FooFunc> func_a, func_b;
+ func_a.reset(func_b.release());
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+ // CHECK-FIXES: func_a = std::move(func_b);
+}
+
+void negatives() {
+ std::unique_ptr<Foo> src;
+ struct OtherDeleter {};
+ std::unique_ptr<Foo, OtherDeleter> dest;
+ dest.reset(src.release());
+
+ std::unique_ptr<Bar, FooFunc> func_a;
+ std::unique_ptr<Bar, BarFunc> func_b;
+ func_a.reset(func_b.release());
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-alias-decls %t
+
+namespace my_namespace {
+class C {};
+}
+
+namespace unused_alias = ::my_namespace; // eol-comments aren't removed (yet)
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: namespace alias decl 'unused_alias' is unused
+// CHECK-FIXES: {{^}}// eol-comments aren't removed (yet)
+
+namespace used_alias = ::my_namespace;
+void f() { used_alias::C c; }
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -- -xc
+
+// Basic removal
+// =============
+void a(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void a(int /*i*/) {}{{$}}
+
+static void b(); // In C, forward declarations can leave out parameters.
+static void b(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}static void b() {}{{$}}
+
+// Unchanged cases
+// ===============
+void h(i, c, d) int i; char *c, *d; {} // Don't mess with K&R style
+
--- /dev/null
+// RUN: echo "static void staticFunctionHeader(int i) {}" > %T/header.h
+// RUN: echo "static void staticFunctionHeader(int /*i*/) {}" > %T/header-fixed.h
+// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -header-filter='.*' -- -std=c++11 -fno-delayed-template-parsing
+// RUN: diff %T/header.h %T/header-fixed.h
+
+#include "header.h"
+// CHECK-MESSAGES: header.h:1:38: warning
+
+// Basic removal
+// =============
+void a(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void a(int /*i*/) {}{{$}}
+
+void b(int i = 1) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void b(int /*i*/) {}{{$}}
+
+void c(int *i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void c(int * /*i*/) {}{{$}}
+
+// Unchanged cases
+// ===============
+void g(int i); // Don't remove stuff in declarations
+void h(int i) { (void)i; } // Don't remove used parameters
+
+bool useLambda(int (*fn)(int));
+static bool static_var = useLambda([] (int a) { return a; });
+
+// Remove parameters of local functions
+// ====================================
+static void staticFunctionA(int i);
+// CHECK-FIXES: {{^}}static void staticFunctionA();
+static void staticFunctionA(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionA()
+
+static void staticFunctionB(int i, int j) { (void)i; }
+// CHECK-MESSAGES: :[[@LINE-1]]:40: warning
+// CHECK-FIXES: {{^}}static void staticFunctionB(int i)
+
+static void staticFunctionC(int i, int j) { (void)j; }
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionC(int j)
+
+static void staticFunctionD(int i, int j, int k) { (void)i; (void)k; }
+// CHECK-MESSAGES: :[[@LINE-1]]:40: warning
+// CHECK-FIXES: {{^}}static void staticFunctionD(int i, int k)
+
+static void staticFunctionE(int i = 4) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionE()
+
+
+static void someCallSites() {
+ staticFunctionA(1);
+// CHECK-FIXES: staticFunctionA();
+ staticFunctionB(1, 2);
+// CHECK-FIXES: staticFunctionB(1);
+ staticFunctionC(1, 2);
+// CHECK-FIXES: staticFunctionC(2);
+ staticFunctionD(1, 2, 3);
+// CHECK-FIXES: staticFunctionD(1, 3);
+ staticFunctionE();
+}
+
+/*
+ * FIXME: This fails because the removals overlap and ClangTidy doesn't apply
+ * them.
+ * static void bothVarsUnused(int a, int b) {}
+ */
+
+// Regression test for long variable names and expressions
+// =======================================================
+static int variableWithLongName1(int LongName1, int LongName2) {
+// CHECK-MESSAGES: :[[@LINE-1]]:53: warning: parameter 'LongName2' is unused
+// CHECK-FIXES: {{^}}static int variableWithLongName1(int LongName1) {
+ return LongName1;
+}
+static int variableWithLongName2(int LongName1, int LongName2) {
+// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: parameter 'LongName1' is unused
+// CHECK-FIXES: {{^}}static int variableWithLongName2(int LongName2) {
+ return LongName2;
+}
+static void someLongNameCallSites() {
+ int LongName1 = 7, LongName2 = 17;
+ variableWithLongName1(LongName1, LongName2);
+// CHECK-FIXES: variableWithLongName1(LongName1);
+ variableWithLongName2(LongName1, LongName2);
+// CHECK-FIXES: variableWithLongName2(LongName2);
+}
+
+class SomeClass {
+ static void f(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning
+// CHECK-FIXES: static void f(int /*i*/) {}
+};
+
+namespace {
+class C {
+public:
+ void f(int i);
+// CHECK-FIXES: void f();
+ void g(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void g() {}
+ void h(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void h(int /*i*/) {}
+};
+
+void C::f(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning
+// CHECK-FIXES: void C::f() {}
+
+template <typename T>
+void useFunction(T t);
+
+void someMoreCallSites() {
+ C c;
+ c.f(1);
+// CHECK-FIXES: c.f();
+ c.g(1);
+// CHECK-FIXES: c.g();
+
+ useFunction(&C::h);
+}
+
+class Base {
+ virtual void f(int i);
+};
+
+class Derived : public Base {
+ void f(int i) override {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void f(int /*i*/) override {}
+};
+
+} // end namespace
+
+template <typename T> void someFunctionTemplate(T b, T e) { (void)b; (void)e; }
+
+template <typename T> void someFunctionTemplateOneUnusedParam(T b, T e) { (void)e; }
+// CHECK-MESSAGES: :[[@LINE-1]]:65: warning
+// CHECK-FIXES: {{^}}template <typename T> void someFunctionTemplateOneUnusedParam(T /*b*/, T e) { (void)e; }
+
+template <typename T> void someFunctionTemplateAllUnusedParams(T b, T e) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:66: warning
+// CHECK-MESSAGES: :[[@LINE-2]]:71: warning
+// CHECK-FIXES: {{^}}template <typename T> void someFunctionTemplateAllUnusedParams(T /*b*/, T /*e*/) {}
+
+static void dontGetConfusedByParametersInFunctionTypes() { void (*F)(int i); }
+
+template <typename T> class Function {};
+static Function<void(int, int i)> dontGetConfusedByFunctionReturnTypes() {
+ return Function<void(int, int)>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-raii %t
+
+struct Foo {
+ Foo();
+ Foo(int);
+ Foo(int, int);
+ ~Foo();
+};
+
+struct Bar {
+ Bar();
+};
+
+struct FooBar {
+ FooBar();
+ Foo f;
+};
+
+template <typename T>
+void qux() {
+ T(42);
+}
+
+template <typename T>
+struct TFoo {
+ TFoo(T);
+ ~TFoo();
+};
+
+Foo f();
+
+struct Ctor {
+ Ctor(int);
+ Ctor() {
+ Ctor(0); // TODO: warn here.
+ }
+};
+
+void test() {
+ Foo(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name(42);
+ Foo(23, 42);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name(23, 42);
+ Foo();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name;
+ TFoo<int>(23);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: TFoo<int> give_me_a_name(23);
+
+ FooBar();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: FooBar give_me_a_name;
+
+ Bar();
+ f();
+ qux<Foo>();
+
+#define M Foo();
+ M
+
+ {
+ Foo();
+ }
+ Foo();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t
+
+namespace n {
+class C;
+}
+
+using n::C;
+
+void f() {
+ for (C *p : unknown()) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: error: use of undeclared identifier 'unknown' [clang-diagnostic-error]
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -- -fno-delayed-template-parsing -isystem %S/Inputs/
+
+
+// ----- Definitions -----
+template <typename T> class vector {};
+namespace n {
+class A;
+class B;
+class C;
+class D;
+class D { public: static int i; };
+template <typename T> class E {};
+template <typename T> class F {};
+class G { public: static void func() {} };
+class H { public: static int i; };
+class I {
+ public:
+ static int ii;
+};
+template <typename T> class J {};
+class G;
+class H;
+
+template <typename T> class K {};
+template <template <typename> class S>
+class L {};
+
+template <typename T> class M {};
+class N {};
+
+template <int T> class P {};
+const int Constant = 0;
+
+class Base {
+ public:
+ void f();
+};
+
+D UsedInstance;
+D UnusedInstance;
+
+int UsedFunc() { return 1; }
+int UnusedFunc() { return 1; }
+template <typename T> int UsedTemplateFunc() { return 1; }
+template <typename T> int UnusedTemplateFunc() { return 1; }
+template <typename T> int UsedInTemplateFunc() { return 1; }
+void OverloadFunc(int);
+void OverloadFunc(double);
+int FuncUsedByUsingDeclInMacro() { return 1; }
+
+class ostream {
+public:
+ ostream &operator<<(ostream &(*PF)(ostream &));
+};
+extern ostream cout;
+ostream &endl(ostream &os);
+
+enum Color1 { Green };
+
+enum Color2 { Red };
+
+enum Color3 { Yellow };
+
+enum Color4 { Blue };
+
+} // namespace n
+
+#include "unused-using-decls.h"
+namespace ns {
+template <typename T>
+class AA {
+ T t;
+};
+template <typename T>
+T ff() { T t; return t; }
+} // namespace ns
+
+// ----- Using declarations -----
+// eol-comments aren't removed (yet)
+using n::A; // A
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'A' is unused
+// CHECK-FIXES: {{^}}// A
+using n::B;
+using n::C;
+using n::D;
+using n::E; // E
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'E' is unused
+// CHECK-FIXES: {{^}}// E
+using n::F;
+using n::G;
+using n::H;
+using n::I;
+int I::ii = 1;
+class Derived : public n::Base {
+ public:
+ using Base::f;
+};
+using n::UsedInstance;
+using n::UsedFunc;
+using n::UsedTemplateFunc;
+using n::UnusedInstance; // UnusedInstance
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'UnusedInstance' is unused
+// CHECK-FIXES: {{^}}// UnusedInstance
+using n::UnusedFunc; // UnusedFunc
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'UnusedFunc' is unused
+// CHECK-FIXES: {{^}}// UnusedFunc
+using n::cout;
+using n::endl;
+
+using n::UsedInTemplateFunc;
+using n::J;
+template <typename T> void Callee() {
+ J<T> j;
+ UsedInTemplateFunc<T>();
+}
+
+using n::OverloadFunc; // OverloadFunc
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'OverloadFunc' is unused
+// CHECK-FIXES: {{^}}// OverloadFunc
+
+#define DEFINE_INT(name) \
+ namespace INT { \
+ static const int _##name = 1; \
+ } \
+ using INT::_##name
+DEFINE_INT(test);
+#undef DEFIND_INT
+
+#define USING_FUNC \
+ using n::FuncUsedByUsingDeclInMacro;
+USING_FUNC
+#undef USING_FUNC
+
+namespace N1 {
+// n::G is used in namespace N2.
+// Currently, the check doesn't support multiple scopes. All the relevant
+// using-decls will be marked as used once we see an usage even the usage is in
+// other scope.
+using n::G;
+}
+
+namespace N2 {
+using n::G;
+void f(G g);
+}
+
+void IgnoreFunctionScope() {
+// Using-decls defined in function scope will be ignored.
+using n::H;
+}
+
+using n::Color1;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Color1' is unused
+using n::Green;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Green' is unused
+using n::Color2;
+using n::Color3;
+using n::Blue;
+
+using ns::AA;
+using ns::ff;
+
+using n::K;
+
+using n::N;
+
+// FIXME: Currently non-type template arguments are not supported.
+using n::Constant;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Constant' is unused
+
+// ----- Usages -----
+void f(B b);
+void g() {
+ vector<C> data;
+ D::i = 1;
+ F<int> f;
+ void (*func)() = &G::func;
+ int *i = &H::i;
+ UsedInstance.i;
+ UsedFunc();
+ UsedTemplateFunc<int>();
+ cout << endl;
+ Color2 color2;
+ int t1 = Color3::Yellow;
+ int t2 = Blue;
+
+ MyClass a;
+ int t3 = 0;
+ a.func1<AA>(&t3);
+ a.func2<int, ff>(t3);
+
+ n::L<K> l;
+}
+
+template<class T>
+void h(n::M<T>* t) {}
+// n::N is used the explicit template instantiation.
+template void h(n::M<N>* t);
+
+// Test on Non-type template arguments.
+template <int T>
+void i(n::P<T>* t) {}
+template void i(n::P<Constant>* t);
--- /dev/null
+// RUN: %check_clang_tidy %s misc-use-after-move %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+typedef decltype(nullptr) nullptr_t;
+
+namespace std {
+typedef unsigned size_t;
+
+template <typename T>
+struct unique_ptr {
+ unique_ptr();
+ T *get() const;
+ explicit operator bool() const;
+ void reset(T *ptr);
+ T &operator*() const;
+ T *operator->() const;
+ T& operator[](size_t i) const;
+};
+
+template <typename T>
+struct shared_ptr {
+ shared_ptr();
+ T *get() const;
+ explicit operator bool() const;
+ void reset(T *ptr);
+ T &operator*() const;
+ T *operator->() const;
+};
+
+template <typename T>
+struct weak_ptr {
+ weak_ptr();
+ bool expired() const;
+};
+
+#define DECLARE_STANDARD_CONTAINER(name) \
+ template <typename T> \
+ struct name { \
+ name(); \
+ void clear(); \
+ bool empty(); \
+ }
+
+#define DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(name) \
+ template <typename T> \
+ struct name { \
+ name(); \
+ void clear(); \
+ bool empty(); \
+ void assign(size_t, const T &); \
+ }
+
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(basic_string);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(vector);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(deque);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(forward_list);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(list);
+DECLARE_STANDARD_CONTAINER(set);
+DECLARE_STANDARD_CONTAINER(map);
+DECLARE_STANDARD_CONTAINER(multiset);
+DECLARE_STANDARD_CONTAINER(multimap);
+DECLARE_STANDARD_CONTAINER(unordered_set);
+DECLARE_STANDARD_CONTAINER(unordered_map);
+DECLARE_STANDARD_CONTAINER(unordered_multiset);
+DECLARE_STANDARD_CONTAINER(unordered_multimap);
+
+typedef basic_string<char> string;
+
+template <typename>
+struct remove_reference;
+
+template <typename _Tp>
+struct remove_reference {
+ typedef _Tp type;
+};
+
+template <typename _Tp>
+struct remove_reference<_Tp &> {
+ typedef _Tp type;
+};
+
+template <typename _Tp>
+struct remove_reference<_Tp &&> {
+ typedef _Tp type;
+};
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept {
+ return static_cast<typename remove_reference<_Tp>::type &&>(__t);
+}
+
+} // namespace std
+
+class A {
+public:
+ A();
+ A(const A &);
+ A(A &&);
+
+ A &operator=(const A &);
+ A &operator=(A &&);
+
+ void foo() const;
+ int getInt() const;
+
+ operator bool() const;
+
+ int i;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// General tests.
+
+// Simple case.
+void simple() {
+ A a;
+ a.foo();
+ A other_a = std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here
+}
+
+// A warning should only be emitted for one use-after-move.
+void onlyFlagOneUseAfterMove() {
+ A a;
+ a.foo();
+ A other_a = std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here
+ a.foo();
+}
+
+void moveAfterMove() {
+ // Move-after-move also counts as a use.
+ {
+ A a;
+ std::move(a);
+ std::move(a);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // This is also true if the move itself turns into the use on the second loop
+ // iteration.
+ {
+ A a;
+ for (int i = 0; i < 10; ++i) {
+ std::move(a);
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use happens in a later loop
+ }
+ }
+}
+
+// Checks also works on function parameters that have a use-after move.
+void parameters(A a) {
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+void standardSmartPtr() {
+ // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to
+ // be null after a std::move. So the check only flags accesses that would
+ // dereference the pointer.
+ {
+ std::unique_ptr<A> ptr;
+ std::move(ptr);
+ ptr.get();
+ static_cast<bool>(ptr);
+ *ptr;
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+ }
+ {
+ std::unique_ptr<A> ptr;
+ std::move(ptr);
+ ptr->foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ {
+ std::unique_ptr<A> ptr;
+ std::move(ptr);
+ ptr[0];
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ {
+ std::shared_ptr<A> ptr;
+ std::move(ptr);
+ ptr.get();
+ static_cast<bool>(ptr);
+ *ptr;
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+ }
+ {
+ std::shared_ptr<A> ptr;
+ std::move(ptr);
+ ptr->foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ {
+ // std::weak_ptr<> cannot be dereferenced directly, so we only check that
+ // member functions may be called on it after a move.
+ std::weak_ptr<A> ptr;
+ std::move(ptr);
+ ptr.expired();
+ }
+ // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're
+ // wrapped in a typedef.
+ {
+ typedef std::unique_ptr<A> PtrToA;
+ PtrToA ptr;
+ std::move(ptr);
+ ptr.get();
+ }
+ {
+ typedef std::shared_ptr<A> PtrToA;
+ PtrToA ptr;
+ std::move(ptr);
+ ptr.get();
+ }
+ // And we don't get confused if the template argument is a little more
+ // involved.
+ {
+ struct B {
+ typedef A AnotherNameForA;
+ };
+ std::unique_ptr<B::AnotherNameForA> ptr;
+ std::move(ptr);
+ ptr.get();
+ }
+ // We don't give any special treatment to types that are called "unique_ptr"
+ // or "shared_ptr" but are not in the "::std" namespace.
+ {
+ struct unique_ptr {
+ void get();
+ } ptr;
+ std::move(ptr);
+ ptr.get();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+}
+
+// The check also works in member functions.
+class Container {
+ void useAfterMoveInMemberFunction() {
+ A a;
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+};
+
+// We see the std::move() if it's inside a declaration.
+void moveInDeclaration() {
+ A a;
+ A another_a(std::move(a));
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+}
+
+// We see the std::move if it's inside an initializer list. Initializer lists
+// are a special case because they cause ASTContext::getParents() to return
+// multiple parents for certain nodes in their subtree. This is because
+// RecursiveASTVisitor visits both the syntactic and semantic forms of
+// InitListExpr, and the parent-child relationships are different between the
+// two forms.
+void moveInInitList() {
+ struct S {
+ A a;
+ };
+ A a;
+ S s{std::move(a)};
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+}
+
+void lambdas() {
+ // Use-after-moves inside a lambda should be detected.
+ {
+ A a;
+ auto lambda = [a] {
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+ };
+ }
+ // This is just as true if the variable was declared inside the lambda.
+ {
+ auto lambda = [] {
+ A a;
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+ };
+ }
+ // But don't warn if the move happened inside the lambda but the use happened
+ // outside -- because
+ // - the 'a' inside the lambda is a copy, and
+ // - we don't know when the lambda will get called anyway
+ {
+ A a;
+ auto lambda = [a] {
+ std::move(a);
+ };
+ a.foo();
+ }
+ // Warn if the use consists of a capture that happens after a move.
+ {
+ A a;
+ std::move(a);
+ auto lambda = [a]() { a.foo(); };
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // ...even if the capture was implicit.
+ {
+ A a;
+ std::move(a);
+ auto lambda = [=]() { a.foo(); };
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // Same tests but for capture by reference.
+ {
+ A a;
+ std::move(a);
+ auto lambda = [&a]() { a.foo(); };
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ {
+ A a;
+ std::move(a);
+ auto lambda = [&]() { a.foo(); };
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // But don't warn if the move happened after the capture.
+ {
+ A a;
+ auto lambda = [a]() { a.foo(); };
+ std::move(a);
+ }
+ // ...and again, same thing with an implicit move.
+ {
+ A a;
+ auto lambda = [=]() { a.foo(); };
+ std::move(a);
+ }
+ // Same tests but for capture by reference.
+ {
+ A a;
+ auto lambda = [&a]() { a.foo(); };
+ std::move(a);
+ }
+ {
+ A a;
+ auto lambda = [&]() { a.foo(); };
+ std::move(a);
+ }
+}
+
+// Use-after-moves are detected in uninstantiated templates if the moved type
+// is not a dependent type.
+template <class T>
+void movedTypeIsNotDependentType() {
+ T t;
+ A a;
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+// And if the moved type is a dependent type, the use-after-move is detected if
+// the template is instantiated.
+template <class T>
+void movedTypeIsDependentType() {
+ T t;
+ std::move(t);
+ t.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+template void movedTypeIsDependentType<A>();
+
+// We handle the case correctly where the move consists of an implicit call
+// to a conversion operator.
+void implicitConversionOperator() {
+ struct Convertible {
+ operator A() && { return A(); }
+ };
+ void takeA(A a);
+
+ Convertible convertible;
+ takeA(std::move(convertible));
+ convertible;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here
+}
+
+// Using decltype on an expression is not a use.
+void decltypeIsNotUse() {
+ A a;
+ std::move(a);
+ decltype(a) other_a;
+}
+
+// Ignore moves or uses that occur as part of template arguments.
+template <int>
+class ClassTemplate {
+public:
+ void foo(A a);
+};
+template <int>
+void functionTemplate(A a);
+void templateArgIsNotUse() {
+ {
+ // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
+ // Google Test.
+ A a;
+ ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a));
+ }
+ {
+ A a;
+ functionTemplate<sizeof(A(std::move(a)))>(std::move(a));
+ }
+}
+
+// Ignore moves of global variables.
+A global_a;
+void ignoreGlobalVariables() {
+ std::move(global_a);
+ global_a.foo();
+}
+
+// Ignore moves of member variables.
+class IgnoreMemberVariables {
+ A a;
+ static A static_a;
+
+ void f() {
+ std::move(a);
+ a.foo();
+
+ std::move(static_a);
+ static_a.foo();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests involving control flow.
+
+void useAndMoveInLoop() {
+ // Warn about use-after-moves if they happen in a later loop iteration than
+ // the std::move().
+ {
+ A a;
+ for (int i = 0; i < 10; ++i) {
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE+2]]:7: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use happens in a later loop
+ std::move(a);
+ }
+ }
+ // However, this case shouldn't be flagged -- the scope of the declaration of
+ // 'a' is important.
+ {
+ for (int i = 0; i < 10; ++i) {
+ A a;
+ a.foo();
+ std::move(a);
+ }
+ }
+ // Same as above, except that we have an unrelated variable being declared in
+ // the same declaration as 'a'. This case is interesting because it tests that
+ // the synthetic DeclStmts generated by the CFG are sequenced correctly
+ // relative to the other statements.
+ {
+ for (int i = 0; i < 10; ++i) {
+ A a, other;
+ a.foo();
+ std::move(a);
+ }
+ }
+ // Don't warn if we return after the move.
+ {
+ A a;
+ for (int i = 0; i < 10; ++i) {
+ a.foo();
+ if (a.getInt() > 0) {
+ std::move(a);
+ return;
+ }
+ }
+ }
+}
+
+void differentBranches(int i) {
+ // Don't warn if the use is in a different branch from the move.
+ {
+ A a;
+ if (i > 0) {
+ std::move(a);
+ } else {
+ a.foo();
+ }
+ }
+ // Same thing, but with a ternary operator.
+ {
+ A a;
+ i > 0 ? (void)std::move(a) : a.foo();
+ }
+ // A variation on the theme above.
+ {
+ A a;
+ a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt();
+ }
+ // Same thing, but with a switch statement.
+ {
+ A a;
+ switch (i) {
+ case 1:
+ std::move(a);
+ break;
+ case 2:
+ a.foo();
+ break;
+ }
+ }
+ // However, if there's a fallthrough, we do warn.
+ {
+ A a;
+ switch (i) {
+ case 1:
+ std::move(a);
+ case 2:
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here
+ break;
+ }
+ }
+}
+
+// False positive: A use-after-move is flagged even though the "if (b)" and
+// "if (!b)" are mutually exclusive.
+void mutuallyExclusiveBranchesFalsePositive(bool b) {
+ A a;
+ if (b) {
+ std::move(a);
+ }
+ if (!b) {
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+ }
+}
+
+// Destructors marked [[noreturn]] are handled correctly in the control flow
+// analysis. (These are used in some styles of assertion macros.)
+class FailureLogger {
+public:
+ FailureLogger();
+ [[noreturn]] ~FailureLogger();
+ void log(const char *);
+};
+#define ASSERT(x) \
+ while (x) \
+ FailureLogger().log(#x)
+bool operationOnA(A);
+void noreturnDestructor() {
+ A a;
+ // The while loop in the ASSERT() would ordinarily have the potential to cause
+ // a use-after-move because the second iteration of the loop would be using a
+ // variable that had been moved from in the first iteration. Check that the
+ // CFG knows that the second iteration of the loop is never reached because
+ // the FailureLogger destructor is marked [[noreturn]].
+ ASSERT(operationOnA(std::move(a)));
+}
+#undef ASSERT
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests for reinitializations
+
+template <class T>
+void swap(T &a, T &b) {
+ T tmp = std::move(a);
+ a = std::move(b);
+ b = std::move(tmp);
+}
+void assignments(int i) {
+ // Don't report a use-after-move if the variable was assigned to in the
+ // meantime.
+ {
+ A a;
+ std::move(a);
+ a = A();
+ a.foo();
+ }
+ // The assignment should also be recognized if move, assignment and use don't
+ // all happen in the same block (but the assignment is still guaranteed to
+ // prevent a use-after-move).
+ {
+ A a;
+ if (i == 1) {
+ std::move(a);
+ a = A();
+ }
+ if (i == 2) {
+ a.foo();
+ }
+ }
+ {
+ A a;
+ if (i == 1) {
+ std::move(a);
+ }
+ if (i == 2) {
+ a = A();
+ a.foo();
+ }
+ }
+ // The built-in assignment operator should also be recognized as a
+ // reinitialization. (std::move() may be called on built-in types in template
+ // code.)
+ {
+ int a1 = 1, a2 = 2;
+ swap(a1, a2);
+ }
+ // A std::move() after the assignment makes the variable invalid again.
+ {
+ A a;
+ std::move(a);
+ a = A();
+ std::move(a);
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // Report a use-after-move if we can't be sure that the variable was assigned
+ // to.
+ {
+ A a;
+ std::move(a);
+ if (i < 10) {
+ a = A();
+ }
+ if (i > 5) {
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here
+ }
+ }
+}
+
+// Passing the object to a function through a non-const pointer or reference
+// counts as a re-initialization.
+void passByNonConstPointer(A *);
+void passByNonConstReference(A &);
+void passByNonConstPointerIsReinit() {
+ {
+ A a;
+ std::move(a);
+ passByNonConstPointer(&a);
+ a.foo();
+ }
+ {
+ A a;
+ std::move(a);
+ passByNonConstReference(a);
+ a.foo();
+ }
+}
+
+// Passing the object through a const pointer or reference counts as a use --
+// since the called function cannot reinitialize the object.
+void passByConstPointer(const A *);
+void passByConstReference(const A &);
+void passByConstPointerIsUse() {
+ {
+ // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the
+ // AST -- we wouldn't want the check to rely solely on that to detect a
+ // const pointer argument.
+ const A a;
+ std::move(a);
+ passByConstPointer(&a);
+ // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ const A a;
+ std::move(a);
+ passByConstReference(a);
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+// Clearing a standard container using clear() is treated as a
+// re-initialization.
+void standardContainerClearIsReinit() {
+ {
+ std::string container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::vector<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::deque<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::forward_list<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::list<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::set<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::map<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::multiset<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::multimap<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::unordered_set<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::unordered_map<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::unordered_multiset<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ {
+ std::unordered_multimap<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ // This should also work for typedefs of standard containers.
+ {
+ typedef std::vector<int> IntVector;
+ IntVector container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ // But it shouldn't work for non-standard containers.
+ {
+ // This might be called "vector", but it's not in namespace "std".
+ struct vector {
+ void clear() {}
+ } container;
+ std::move(container);
+ container.clear();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // An intervening clear() on a different container does not reinitialize.
+ {
+ std::vector<int> container1, container2;
+ std::move(container1);
+ container2.clear();
+ container1.empty();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was
+ // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here
+ }
+}
+
+// Clearing a standard container using assign() is treated as a
+// re-initialization.
+void standardContainerAssignIsReinit() {
+ {
+ std::string container;
+ std::move(container);
+ container.assign(0, ' ');
+ container.empty();
+ }
+ {
+ std::vector<int> container;
+ std::move(container);
+ container.assign(0, 0);
+ container.empty();
+ }
+ {
+ std::deque<int> container;
+ std::move(container);
+ container.assign(0, 0);
+ container.empty();
+ }
+ {
+ std::forward_list<int> container;
+ std::move(container);
+ container.assign(0, 0);
+ container.empty();
+ }
+ {
+ std::list<int> container;
+ std::move(container);
+ container.clear();
+ container.empty();
+ }
+ // But it doesn't work for non-standard containers.
+ {
+ // This might be called "vector", but it's not in namespace "std".
+ struct vector {
+ void assign(std::size_t, int) {}
+ } container;
+ std::move(container);
+ container.assign(0, 0);
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ // An intervening assign() on a different container does not reinitialize.
+ {
+ std::vector<int> container1, container2;
+ std::move(container1);
+ container2.assign(0, 0);
+ container1.empty();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was
+ // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here
+ }
+}
+
+// Resetting the standard smart pointer types using reset() is treated as a
+// re-initialization. (We don't test std::weak_ptr<> because it can't be
+// dereferenced directly.)
+void standardSmartPointerResetIsReinit() {
+ {
+ std::unique_ptr<A> ptr;
+ std::move(ptr);
+ ptr.reset(new A);
+ *ptr;
+ }
+ {
+ std::shared_ptr<A> ptr;
+ std::move(ptr);
+ ptr.reset(new A);
+ *ptr;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests related to order of evaluation within expressions
+
+// Relative sequencing of move and use.
+void passByRvalueReference(int i, A &&a);
+void passByValue(int i, A a);
+void passByValue(A a, int i);
+A g(A, A &&);
+int intFromA(A &&);
+int intFromInt(int);
+void sequencingOfMoveAndUse() {
+ // This case is fine because the move only happens inside
+ // passByRvalueReference(). At this point, a.getInt() is guaranteed to have
+ // been evaluated.
+ {
+ A a;
+ passByRvalueReference(a.getInt(), std::move(a));
+ }
+ // However, if we pass by value, the move happens when the move constructor is
+ // called to create a temporary, and this happens before the call to
+ // passByValue(). Because the order in which arguments are evaluated isn't
+ // defined, the move may happen before the call to a.getInt().
+ //
+ // Check that we warn about a potential use-after move for both orderings of
+ // a.getInt() and std::move(a), independent of the order in which the
+ // arguments happen to get evaluated by the compiler.
+ {
+ A a;
+ passByValue(a.getInt(), std::move(a));
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced
+ }
+ {
+ A a;
+ passByValue(std::move(a), a.getInt());
+ // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced
+ }
+ // An even more convoluted example.
+ {
+ A a;
+ g(g(a, std::move(a)), g(a, std::move(a)));
+ // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced
+ // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced
+ }
+ // This case is fine because the actual move only happens inside the call to
+ // operator=(). a.getInt(), by necessity, is evaluated before that call.
+ {
+ A a;
+ A vec[1];
+ vec[a.getInt()] = std::move(a);
+ }
+ // However, in the following case, the move happens before the assignment, and
+ // so the order of evaluation is not guaranteed.
+ {
+ A a;
+ int v[3];
+ v[a.getInt()] = intFromA(std::move(a));
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced
+ }
+ {
+ A a;
+ int v[3];
+ v[intFromA(std::move(a))] = intFromInt(a.i);
+ // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here
+ // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced
+ }
+}
+
+// Relative sequencing of move and reinitialization. If the two are unsequenced,
+// we conservatively assume that the move happens after the reinitialization,
+// i.e. the that object does not get reinitialized after the move.
+A MutateA(A a);
+void passByValue(A a1, A a2);
+void sequencingOfMoveAndReinit() {
+ // Move and reinitialization as function arguments (which are indeterminately
+ // sequenced). Again, check that we warn for both orderings.
+ {
+ A a;
+ passByValue(std::move(a), (a = A()));
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here
+ }
+ {
+ A a;
+ passByValue((a = A()), std::move(a));
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here
+ }
+ // Common usage pattern: Move the object to a function that mutates it in some
+ // way, then reassign the result to the object. This pattern is fine.
+ {
+ A a;
+ a = MutateA(std::move(a));
+ a.foo();
+ }
+}
+
+// Relative sequencing of reinitialization and use. If the two are unsequenced,
+// we conservatively assume that the reinitialization happens after the use,
+// i.e. that the object is not reinitialized at the point in time when it is
+// used.
+void sequencingOfReinitAndUse() {
+ // Reinitialization and use in function arguments. Again, check both possible
+ // orderings.
+ {
+ A a;
+ std::move(a);
+ passByValue(a.getInt(), (a = A()));
+ // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+ {
+ A a;
+ std::move(a);
+ passByValue((a = A()), a.getInt());
+ // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+ }
+}
+
+// The comma operator sequences its operands.
+void commaOperatorSequences() {
+ {
+ A a;
+ A(std::move(a))
+ , (a = A());
+ a.foo();
+ }
+ {
+ A a;
+ (a = A()), A(std::move(a));
+ a.foo();
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here
+ }
+}
+
+// An initializer list sequences its initialization clauses.
+void initializerListSequences() {
+ {
+ struct S1 {
+ int i;
+ A a;
+ };
+ A a;
+ S1 s1{a.getInt(), std::move(a)};
+ }
+ {
+ struct S2 {
+ A a;
+ int i;
+ };
+ A a;
+ S2 s2{std::move(a), a.getInt()};
+ // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here
+ }
+}
+
+// A declaration statement containing multiple declarations sequences the
+// initializer expressions.
+void declarationSequences() {
+ {
+ A a;
+ A a1 = a, a2 = std::move(a);
+ }
+ {
+ A a;
+ A a1 = std::move(a), a2 = a;
+ // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here
+ }
+}
+
+// The logical operators && and || sequence their operands.
+void logicalOperatorsSequence() {
+ {
+ A a;
+ if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) {
+ A().foo();
+ }
+ }
+ // A variation: Negate the result of the && (which pushes the && further down
+ // into the AST).
+ {
+ A a;
+ if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) {
+ A().foo();
+ }
+ }
+ {
+ A a;
+ if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) {
+ // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here
+ A().foo();
+ }
+ }
+ {
+ A a;
+ if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) {
+ A().foo();
+ }
+ }
+ {
+ A a;
+ if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) {
+ // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved
+ // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here
+ A().foo();
+ }
+ }
+}
+
+// A range-based for sequences the loop variable declaration before the body.
+void forRangeSequences() {
+ A v[2] = {A(), A()};
+ for (A &a : v) {
+ std::move(a);
+ }
+}
+
+// If a variable is declared in an if statement, the declaration of the variable
+// (which is treated like a reinitialization by the check) is sequenced before
+// the evaluation of the condition (which constitutes a use).
+void ifStmtSequencesDeclAndCondition() {
+ for (int i = 0; i < 10; ++i) {
+ if (A a = A()) {
+ std::move(a);
+ }
+ }
+}
+
+namespace PR33020 {
+class D {
+ ~D();
+};
+struct A {
+ D d;
+};
+class B {
+ A a;
+};
+template <typename T>
+class C : T, B {
+ void m_fn1() {
+ int a;
+ std::move(a);
+ C c;
+ }
+};
+}
--- /dev/null
+// RUN: %check_clang_tidy %s misc-virtual-near-miss %t
+
+class NoDefinedClass1;
+class NoDefinedClass2;
+
+struct Base {
+ virtual void func();
+ virtual void gunk();
+ virtual ~Base();
+ virtual Base &operator=(const Base &);
+ virtual NoDefinedClass1 *f();
+};
+
+struct Derived : Base {
+ // Should not warn "do you want to override 'gunk'?", because gunk is already
+ // overriden by this class.
+ virtual void funk();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? [misc-virtual-near-miss]
+ // CHECK-FIXES: virtual void func();
+
+ void func2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func'
+ // CHECK-FIXES: void func();
+
+ void func22(); // Should not warn.
+
+ void gunk(); // Should not warn: gunk is override.
+
+ void fun();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func'
+ // CHECK-FIXES: void func();
+
+ Derived &operator==(const Base &); // Should not warn: operators are ignored.
+
+ virtual NoDefinedClass2 *f1(); // Should not crash: non-defined class return type is ignored.
+};
+
+template <typename T>
+struct TBase {
+ virtual void tfunc(T t);
+};
+
+template <typename T>
+struct TDerived : TBase<T> {
+ virtual void tfunk(T t);
+ // Should not apply fix for template.
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: method 'TDerived<double>::tfunk' has {{.*}} 'TBase<double>::tfunc'
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: method 'TDerived<int>::tfunk' has {{.*}} 'TBase<int>::tfunc'
+ // CHECK-FIXES: virtual void tfunk(T t);
+};
+
+TDerived<int> T1;
+TDerived<double> T2;
+
+// Should not fix macro definition
+#define MACRO1 void funcM()
+// CHECK-FIXES: #define MACRO1 void funcM()
+#define MACRO2(m) void m()
+// CHECK-FIXES: #define MACRO2(m) void m()
+
+struct DerivedMacro : Base {
+ MACRO1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::funcM' has {{.*}} 'Base::func'
+ // CHECK-FIXES: MACRO1;
+
+ MACRO2(func3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::func3' has {{.*}} 'Base::func'
+ // CHECK-FIXES: MACRO2(func);
+};
+
+typedef Derived derived_type;
+
+class Father {
+public:
+ Father();
+ virtual void func();
+ virtual Father *create(int i);
+ virtual Base &&generate();
+ virtual Base *canonical(Derived D);
+};
+
+class Mother {
+public:
+ Mother();
+ static void method();
+ virtual int method(int argc, const char **argv);
+ virtual int method(int argc) const;
+ virtual int decay(const char *str);
+};
+
+class Child : private Father, private Mother {
+public:
+ Child();
+
+ virtual void func2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func'
+ // CHECK-FIXES: virtual void func();
+
+ int methoe(int x, char **strs); // Should not warn: parameter types don't match.
+
+ int methoe(int x);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoe' has {{.*}} 'Mother::method'
+ // CHECK-FIXES: int method(int x);
+
+ void methof(int x, const char **strs); // Should not warn: return types don't match.
+
+ int methoh(int x, const char **strs);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoh' has {{.*}} 'Mother::method'
+ // CHECK-FIXES: int method(int x, const char **strs);
+
+ virtual Child *creat(int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create'
+ // CHECK-FIXES: virtual Child *create(int i);
+
+ virtual Derived &&generat();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate'
+ // CHECK-FIXES: virtual Derived &&generate();
+
+ int decaz(const char str[]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::decaz' has {{.*}} 'Mother::decay'
+ // CHECK-FIXES: int decay(const char str[]);
+
+ operator bool();
+
+ derived_type *canonica(derived_type D);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::canonica' has {{.*}} 'Father::canonical'
+ // CHECK-FIXES: derived_type *canonical(derived_type D);
+
+private:
+ void funk();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::funk' has {{.*}} 'Father::func'
+ // CHECK-FIXES: void func();
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-avoid-bind %t -- -- -std=c++14
+
+namespace std {
+inline namespace impl {
+template <class Fp, class... Arguments>
+class bind_rt {};
+
+template <class Fp, class... Arguments>
+bind_rt<Fp, Arguments...> bind(Fp &&, Arguments &&...);
+}
+}
+
+int add(int x, int y) { return x + y; }
+
+void f() {
+ auto clj = std::bind(add, 2, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind [modernize-avoid-bind]
+ // CHECK-FIXES: auto clj = [] { return add(2, 2); };
+}
+
+void g() {
+ int x = 2;
+ int y = 2;
+ auto clj = std::bind(add, x, y);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // CHECK-FIXES: auto clj = [=] { return add(x, y); };
+}
+
+struct placeholder {};
+placeholder _1;
+placeholder _2;
+
+void h() {
+ int x = 2;
+ auto clj = std::bind(add, x, _1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // CHECK-FIXES: auto clj = [=](auto && arg1) { return add(x, arg1); };
+}
+
+struct A;
+struct B;
+bool ABTest(const A &, const B &);
+
+void i() {
+ auto BATest = std::bind(ABTest, _2, _1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer a lambda to std::bind
+ // CHECK-FIXES: auto BATest = [](auto && arg1, auto && arg2) { return ABTest(arg2, arg1); };
+}
+
+void j() {
+ auto clj = std::bind(add, 2, 2, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for argument mismatches.
+ // CHECK-FIXES: auto clj = std::bind(add, 2, 2, 2);
+}
+
+void k() {
+ auto clj = std::bind(add, _1, _1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for reused placeholders.
+ // CHECK-FIXES: auto clj = std::bind(add, _1, _1);
+}
+
+void m() {
+ auto clj = std::bind(add, 1, add(2, 5));
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // No fix is applied for nested calls.
+ // CHECK-FIXES: auto clj = std::bind(add, 1, add(2, 5));
+}
+
+namespace C {
+ int add(int x, int y){ return x + y; }
+}
+
+void n() {
+ auto clj = std::bind(C::add, 1, 1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+ // CHECK-FIXES: auto clj = [] { return C::add(1, 1); };
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-deprecated-headers %t -- -extra-arg-before=-isystem%S/Inputs/modernize-deprecated-headers -- -std=c++03 -v
+
+#include <assert.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include <complex.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include <ctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include <errno.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include <float.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include <limits.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include <locale.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include <math.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include <setjmp.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include <signal.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include <stdarg.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include <stddef.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include <stdio.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include <stdlib.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include <string.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include <time.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include <wchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include <wctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>{{$}}
+
+// Headers that have no effect in C++; remove them
+#include <stdalign.h> // <stdalign.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdalign.h>{{$}}
+#include <stdbool.h> // <stdbool.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdbool.h>{{$}}
+#include <iso646.h> // <iso646.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <iso646.h>{{$}}
+
+// Headers deprecated since C++11: expect no diagnostics.
+#include <fenv.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <tgmath.h>
+#include <uchar.h>
+
+
+#include "assert.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include "complex.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include "ctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include "errno.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include "float.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include "limits.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include "locale.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include "math.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include "setjmp.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include "signal.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include "stdarg.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include "stddef.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include "stdio.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include "stdlib.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include "string.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include "time.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include "wchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include "wctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include "stdalign.h" // "stdalign.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdalign.h"{{$}}
+#include "stdbool.h" // "stdbool.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdbool.h"{{$}}
+#include "iso646.h" // "iso646.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "iso646.h"{{$}}
+
+// Headers deprecated since C++11; expect no diagnostics
+#include "fenv.h"
+#include "inttypes.h"
+#include "stdint.h"
+#include "tgmath.h"
+#include "uchar.h"
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-deprecated-headers %t -- -extra-arg-before=-isystem%S/Inputs/modernize-deprecated-headers -- -std=c++11 -v
+
+#include <assert.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include <complex.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include <ctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include <errno.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include <fenv.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-FIXES: {{^}}#include <cfenv>{{$}}
+#include <float.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include <inttypes.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-FIXES: {{^}}#include <cinttypes>{{$}}
+#include <limits.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include <locale.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include <math.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include <setjmp.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include <signal.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include <stdarg.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include <stddef.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include <stdint.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-FIXES: {{^}}#include <cstdint>{{$}}
+#include <stdio.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include <stdlib.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include <string.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include <tgmath.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-FIXES: {{^}}#include <ctgmath>{{$}}
+#include <time.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include <uchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-FIXES: {{^}}#include <cuchar>{{$}}
+#include <wchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include <wctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include <stdalign.h> // <stdalign.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdalign.h>{{$}}
+#include <stdbool.h> // <stdbool.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdbool.h>{{$}}
+#include <iso646.h> // <iso646.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <iso646.h>{{$}}
+
+#include "assert.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include "complex.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include "ctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include "errno.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include "fenv.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-FIXES: {{^}}#include <cfenv>{{$}}
+#include "float.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include "inttypes.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-FIXES: {{^}}#include <cinttypes>{{$}}
+#include "limits.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include "locale.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include "math.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include "setjmp.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include "signal.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include "stdarg.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include "stddef.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include "stdint.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-FIXES: {{^}}#include <cstdint>{{$}}
+#include "stdio.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include "stdlib.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include "string.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include "tgmath.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-FIXES: {{^}}#include <ctgmath>{{$}}
+#include "time.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include "uchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-FIXES: {{^}}#include <cuchar>{{$}}
+#include "wchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include "wctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include "stdalign.h" // "stdalign.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdalign.h"{{$}}
+#include "stdbool.h" // "stdbool.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdbool.h"{{$}}
+#include "iso646.h" // "iso646.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "iso646.h"{{$}}
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,modernize-loop-convert --
+
+// Note: this test expects no assert failure happened in clang-tidy.
+
+class LinguisticItem {
+ LinguisticItem *x0;
+ class x1 {
+ bool operator!= ( const x1 &;
+ operator* ( ;
+ LinguisticItem * &operator-> ( ;
+ operator++ (
+ } begin() const;
+ x1 end() const {
+ LinguisticStream x2;
+ for (x1 x3 = x2.begin x3 != x2.end; ++x3)
+ x3->x0
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+namespace Array {
+
+const int N = 6;
+const int NMinusOne = N - 1;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+const int ConstArr[N] = {1, 2, 3, 4, 5, 6};
+int (*PArr)[N] = &Arr;
+
+void f() {
+ int Sum = 0;
+
+ for (int I = 0; I < N; ++I) {
+ Sum += Arr[I];
+ int K;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: Sum += I;
+ // CHECK-FIXES-NEXT: int K;
+
+ for (int I = 0; I < N; ++I) {
+ printf("Fibonacci number is %d\n", Arr[I]);
+ Sum += Arr[I] + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ for (int I = 0; I < N; ++I) {
+ int X = Arr[I];
+ int Y = Arr[I] + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: int X = I;
+ // CHECK-FIXES-NEXT: int Y = I + 2;
+
+ for (int I = 0; I < N; ++I) {
+ int X = N;
+ X = Arr[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: int X = N;
+ // CHECK-FIXES-NEXT: X = I;
+
+ for (int I = 0; I < N; ++I) {
+ Arr[I] += 1;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: I += 1;
+
+ for (int I = 0; I < N; ++I) {
+ int X = Arr[I] + 2;
+ Arr[I]++;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: int X = I + 2;
+ // CHECK-FIXES-NEXT: I++;
+
+ for (int I = 0; I < N; ++I) {
+ Arr[I] = 4 + Arr[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: I = 4 + I;
+
+ for (int I = 0; I < NMinusOne + 1; ++I) {
+ Sum += Arr[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: Sum += I;
+
+ for (int I = 0; I < N; ++I) {
+ printf("Fibonacci number %d has address %p\n", Arr[I], &Arr[I]);
+ Sum += Arr[I] + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number %d has address %p\n", I, &I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ Val Teas[N];
+ for (int I = 0; I < N; ++I) {
+ Teas[I].g();
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Tea : Teas)
+ // CHECK-FIXES-NEXT: Tea.g();
+}
+
+const int *constArray() {
+ for (int I = 0; I < N; ++I) {
+ printf("2 * %d = %d\n", ConstArr[I], ConstArr[I] + ConstArr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : ConstArr)
+ // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I, I + I);
+
+ const NonTriviallyCopyable NonCopy[N]{};
+ for (int I = 0; I < N; ++I) {
+ printf("2 * %d = %d\n", NonCopy[I].X, NonCopy[I].X + NonCopy[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : NonCopy)
+ // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I.X, I.X + I.X);
+
+ const TriviallyCopyableButBig Big[N]{};
+ for (int I = 0; I < N; ++I) {
+ printf("2 * %d = %d\n", Big[I].X, Big[I].X + Big[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : Big)
+ // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I.X, I.X + I.X);
+
+ bool Something = false;
+ for (int I = 0; I < N; ++I) {
+ if (Something)
+ return &ConstArr[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const int & I : ConstArr)
+ // CHECK-FIXES-NEXT: if (Something)
+ // CHECK-FIXES-NEXT: return &I;
+}
+
+struct HasArr {
+ int Arr[N];
+ Val ValArr[N];
+ void implicitThis() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d", Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: printf("%d", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d", ValArr[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : ValArr)
+ // CHECK-FIXES-NEXT: printf("%d", I.X);
+ }
+
+ void explicitThis() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d", this->Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : this->Arr)
+ // CHECK-FIXES-NEXT: printf("%d", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d", this->ValArr[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : this->ValArr)
+ // CHECK-FIXES-NEXT: printf("%d", I.X);
+ }
+};
+
+struct HasIndirectArr {
+ HasArr HA;
+ void implicitThis() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d", HA.Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : HA.Arr)
+ // CHECK-FIXES-NEXT: printf("%d", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d", HA.ValArr[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : HA.ValArr)
+ // CHECK-FIXES-NEXT: printf("%d", I.X);
+ }
+
+ void explicitThis() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d", this->HA.Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : this->HA.Arr)
+ // CHECK-FIXES-NEXT: printf("%d", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d", this->HA.ValArr[I].X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : this->HA.ValArr)
+ // CHECK-FIXES-NEXT: printf("%d", I.X);
+ }
+};
+
+// Loops whose bounds are value-dependent should not be converted.
+template <int N>
+void dependentExprBound() {
+ for (int I = 0; I < N; ++I)
+ Arr[I] = 0;
+}
+template void dependentExprBound<20>();
+
+void memberFunctionPointer() {
+ Val V;
+ void (Val::*mfpArr[N])(void) = {&Val::g};
+ for (int I = 0; I < N; ++I)
+ (V.*mfpArr[I])();
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : mfpArr)
+ // CHECK-FIXES-NEXT: (V.*I)();
+
+ struct Foo {
+ int (Val::*f)();
+ } Foo[N];
+
+ for (int I = 0; I < N; ++I)
+ int R = (V.*(Foo[I].f))();
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Foo)
+ // CHECK-FIXES-NEXT: int R = (V.*(I.f))();
+
+}
+
+} // namespace Array
+
+namespace Iterator {
+
+void f() {
+ /// begin()/end() - based for loops here:
+ T Tt;
+ for (T::iterator It = Tt.begin(), E = Tt.end(); It != E; ++It) {
+ printf("I found %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : Tt)
+ // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+ T *Pt;
+ for (T::iterator It = Pt->begin(), E = Pt->end(); It != E; ++It) {
+ printf("I found %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : *Pt)
+ // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+ S Ss;
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ S *Ps;
+ for (S::iterator It = Ps->begin(), E = Ps->end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & P : *Ps)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+ for (S::const_iterator It = Ss.cbegin(), E = Ss.cend(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ printf("s has value %d\n", It->X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ It->X = 3;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.X = 3;
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ (*It).X = 3;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.X = 3;
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ It->nonConstFun(4, 5);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.nonConstFun(4, 5);
+
+ U Uu;
+ for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+ printf("s has value %d\n", It->X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Uu)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Uu)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+ Val* a = It.operator->();
+ }
+
+ U::iterator A;
+ for (U::iterator I = Uu.begin(), E = Uu.end(); I != E; ++I)
+ int K = A->X + I->X;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Uu)
+ // CHECK-FIXES-NEXT: int K = A->X + I.X;
+
+ dependent<int> V;
+ for (dependent<int>::iterator It = V.begin(), E = V.end();
+ It != E; ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+ for (dependent<int>::iterator It(V.begin()), E = V.end();
+ It != E; ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+ doublyDependent<int, int> Intmap;
+ for (doublyDependent<int, int>::iterator It = Intmap.begin(), E = Intmap.end();
+ It != E; ++It) {
+ printf("Intmap[%d] = %d", It->first, It->second);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Intmap)
+ // CHECK-FIXES: printf("Intmap[%d] = %d", It.first, It.second);
+
+ // PtrSet's iterator dereferences by value so auto & can't be used.
+ {
+ PtrSet<int *> Val_int_ptrs;
+ for (PtrSet<int *>::iterator I = Val_int_ptrs.begin(),
+ E = Val_int_ptrs.end();
+ I != E; ++I) {
+ (void) *I;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto Val_int_ptr : Val_int_ptrs)
+ }
+
+ // This container uses an iterator where the derefence type is a typedef of
+ // a reference type. Make sure non-const auto & is still used. A failure here
+ // means canonical types aren't being tested.
+ {
+ TypedefDerefContainer<int> Int_ptrs;
+ for (TypedefDerefContainer<int>::iterator I = Int_ptrs.begin(),
+ E = Int_ptrs.end();
+ I != E; ++I) {
+ (void) *I;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & Int_ptr : Int_ptrs)
+ }
+
+ {
+ // Iterators returning an rvalue reference should disqualify the loop from
+ // transformation.
+ RValueDerefContainer<int> Container;
+ for (RValueDerefContainer<int>::iterator I = Container.begin(),
+ E = Container.end();
+ I != E; ++I) {
+ (void) *I;
+ }
+ }
+
+ dependent<Val *> Dpp;
+ for (dependent<Val *>::iterator I = Dpp.begin(), E = Dpp.end(); I != E; ++I) {
+ printf("%d\n", (**I).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Dpp)
+ // CHECK-FIXES-NEXT: printf("%d\n", (*I).X);
+
+ for (dependent<Val *>::iterator I = Dpp.begin(), E = Dpp.end(); I != E; ++I) {
+ printf("%d\n", (*I)->X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Dpp)
+ // CHECK-FIXES-NEXT: printf("%d\n", I->X);
+}
+
+// Tests to verify the proper use of auto where the init variable type and the
+// initializer type differ or are mostly the same except for const qualifiers.
+void different_type() {
+ // Ss.begin() returns a type 'iterator' which is just a non-const pointer and
+ // differs from const_iterator only on the const qualification.
+ S Ss;
+ for (S::const_iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ S *Ps;
+ for (S::const_iterator It = Ps->begin(), E = Ps->end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto P : *Ps)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+ // V.begin() returns a user-defined type 'iterator' which, since it's
+ // different from const_iterator, disqualifies these loops from
+ // transformation.
+ dependent<int> V;
+ for (dependent<int>::const_iterator It = V.begin(), E = V.end();
+ It != E; ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+
+ for (dependent<int>::const_iterator It(V.begin()), E = V.end();
+ It != E; ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+}
+
+// Tests to ensure that an implicit 'this' is picked up as the container.
+// If member calls are made to 'this' within the loop, the transform becomes
+// risky as these calls may affect state that affects the loop.
+class C {
+public:
+ typedef MutableVal *iterator;
+ typedef const MutableVal *const_iterator;
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ void doSomething();
+ void doSomething() const;
+
+ void doLoop() {
+ for (iterator I = begin(), E = end(); I != E; ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : *this)
+
+ for (iterator I = C::begin(), E = C::end(); I != E; ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : *this)
+
+ for (iterator I = begin(), E = end(); I != E; ++I) {
+ (void) *I;
+ doSomething();
+ }
+
+ for (iterator I = begin(); I != end(); ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : *this)
+
+ for (iterator I = begin(); I != end(); ++I) {
+ (void) *I;
+ doSomething();
+ }
+ }
+
+ void doLoop() const {
+ for (const_iterator I = begin(), E = end(); I != E; ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto I : *this)
+
+ for (const_iterator I = C::begin(), E = C::end(); I != E; ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto I : *this)
+
+ for (const_iterator I = begin(), E = end(); I != E; ++I) {
+ (void) *I;
+ doSomething();
+ }
+ }
+};
+
+class C2 {
+public:
+ typedef MutableVal *iterator;
+
+ iterator begin() const;
+ iterator end() const;
+
+ void doLoop() {
+ // The implicit 'this' will have an Implicit cast to const C2* wrapped
+ // around it. Make sure the replacement still happens.
+ for (iterator I = begin(), E = end(); I != E; ++I)
+ (void) *I;
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : *this)
+ }
+};
+
+} // namespace Iterator
+
+namespace PseudoArray {
+
+const int N = 6;
+dependent<int> V;
+dependent<int> *Pv;
+const dependent<NonTriviallyCopyable> Constv;
+const dependent<NonTriviallyCopyable> *Pconstv;
+
+transparent<dependent<int>> Cv;
+
+void f() {
+ int Sum = 0;
+ for (int I = 0, E = V.size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", V[I]);
+ Sum += V[I] + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ for (int I = 0, E = V.size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", V.at(I));
+ Sum += V.at(I) + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ for (int I = 0, E = Pv->size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", Pv->at(I));
+ Sum += Pv->at(I) + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : *Pv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ // This test will fail if size() isn't called repeatedly, since it
+ // returns unsigned int, and 0 is deduced to be signed int.
+ // FIXME: Insert the necessary explicit conversion, or write out the types
+ // explicitly.
+ for (int I = 0; I < Pv->size(); ++I) {
+ printf("Fibonacci number is %d\n", (*Pv).at(I));
+ Sum += (*Pv)[I] + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : *Pv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+
+ for (int I = 0; I < Cv->size(); ++I) {
+ printf("Fibonacci number is %d\n", Cv->at(I));
+ Sum += Cv->at(I) + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : *Cv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2;
+}
+
+// Ensure that 'const auto &' is used with containers of non-trivial types.
+void constness() {
+ int Sum = 0;
+ for (int I = 0, E = Constv.size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", Constv[I].X);
+ Sum += Constv[I].X + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : Constv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+ // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+ for (int I = 0, E = Constv.size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", Constv.at(I).X);
+ Sum += Constv.at(I).X + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : Constv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+ // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+ for (int I = 0, E = Pconstv->size(); I < E; ++I) {
+ printf("Fibonacci number is %d\n", Pconstv->at(I).X);
+ Sum += Pconstv->at(I).X + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : *Pconstv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+ // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+ // This test will fail if size() isn't called repeatedly, since it
+ // returns unsigned int, and 0 is deduced to be signed int.
+ // FIXME: Insert the necessary explicit conversion, or write out the types
+ // explicitly.
+ for (int I = 0; I < Pconstv->size(); ++I) {
+ printf("Fibonacci number is %d\n", (*Pconstv).at(I).X);
+ Sum += (*Pconstv)[I].X + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (const auto & I : *Pconstv)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+ // CHECK-FIXES-NEXT: Sum += I.X + 2;
+}
+
+void constRef(const dependent<int>& ConstVRef) {
+ int sum = 0;
+ // FIXME: This does not work with size_t (probably due to the implementation
+ // of dependent); make dependent work exactly like a std container type.
+ for (int I = 0; I < ConstVRef.size(); ++I) {
+ sum += ConstVRef[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : ConstVRef)
+ // CHECK-FIXES-NEXT: sum += I;
+
+ for (auto I = ConstVRef.begin(), E = ConstVRef.end(); I != E; ++I) {
+ sum += *I;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : ConstVRef)
+ // CHECK-FIXES-NEXT: sum += I;
+}
+
+// Check for loops that don't mention containers.
+void noContainer() {
+ for (auto I = 0; I < V.size(); ++I) {
+ }
+
+ for (auto I = 0; I < V.size(); ++I)
+ ;
+}
+
+struct NoBeginEnd {
+ unsigned size() const;
+ unsigned& operator[](int);
+ const unsigned& operator[](int) const;
+};
+
+struct NoConstBeginEnd {
+ NoConstBeginEnd();
+ unsigned size() const;
+ unsigned* begin();
+ unsigned* end();
+ unsigned& operator[](int);
+ const unsigned& operator[](int) const;
+};
+
+struct ConstBeginEnd {
+ ConstBeginEnd();
+ unsigned size() const;
+ unsigned* begin() const;
+ unsigned* end() const;
+ unsigned& operator[](int);
+ const unsigned& operator[](int) const;
+};
+
+// Shouldn't transform pseudo-array uses if the container doesn't provide
+// begin() and end() of the right const-ness.
+void NoBeginEndTest() {
+ NoBeginEnd NBE;
+ for (unsigned I = 0, E = NBE.size(); I < E; ++I)
+ printf("%d\n", NBE[I]);
+
+ const NoConstBeginEnd Const_NCBE;
+ for (unsigned I = 0, E = Const_NCBE.size(); I < E; ++I)
+ printf("%d\n", Const_NCBE[I]);
+
+ ConstBeginEnd CBE;
+ for (unsigned I = 0, E = CBE.size(); I < E; ++I)
+ printf("%d\n", CBE[I]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (unsigned int I : CBE)
+ // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+ const ConstBeginEnd Const_CBE;
+ for (unsigned I = 0, E = Const_CBE.size(); I < E; ++I)
+ printf("%d\n", Const_CBE[I]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (unsigned int I : Const_CBE)
+ // CHECK-FIXES-NEXT: printf("%d\n", I);
+}
+
+struct DerefByValue {
+ DerefByValue();
+ struct iter { unsigned operator*(); };
+ unsigned size() const;
+ iter begin();
+ iter end();
+ unsigned operator[](int);
+};
+
+void derefByValueTest() {
+ DerefByValue DBV;
+ for (unsigned I = 0, E = DBV.size(); I < E; ++I) {
+ printf("%d\n", DBV[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (unsigned int I : DBV)
+ // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+ for (unsigned I = 0, E = DBV.size(); I < E; ++I) {
+ auto f = [DBV, I]() {};
+ printf("%d\n", DBV[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (unsigned int I : DBV)
+ // CHECK-FIXES-NEXT: auto f = [DBV, &I]() {};
+ // CHECK-FIXES-NEXT: printf("%d\n", I);
+}
+
+void fundamentalTypesTest() {
+ const int N = 10;
+ bool Bools[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Bools[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (bool Bool : Bools)
+
+ int Ints[N];
+ unsigned short int Shorts[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Shorts[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (unsigned short Short : Shorts)
+
+ signed long Longs[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Longs[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (long Long : Longs)
+
+ long long int LongLongs[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", LongLongs[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (long long LongLong : LongLongs)
+
+ char Chars[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Chars[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (char Char : Chars)
+
+ wchar_t WChars[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", WChars[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (wchar_t WChar : WChars)
+
+ float Floats[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Floats[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (float Float : Floats)
+
+ double Doubles[N];
+ for (int i = 0; i < N; ++i)
+ printf("%d", Doubles[i]);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (double Double : Doubles)
+}
+
+} // namespace PseudoArray
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'camelBack'}]}" \
+// RUN: -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int n = 10;
+int arr[n];
+int nums[n];
+
+void naming() {
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", arr[i]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+ // CHECK-FIXES: for (int i : arr)
+ // CHECK-FIXES-NEXT: printf("%d\n", i);
+
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", nums[i]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int num : nums)
+ // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+ int num = 0;
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", nums[i] + num);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int i : nums)
+ // CHECK-FIXES-NEXT: printf("%d\n", i + num);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11
+
+struct Str {
+ Str() = default;
+ Str(const Str &) = default;
+ void constMember(int) const;
+ void nonConstMember(int);
+ bool operator<(const Str &str) const; // const operator.
+ Str &operator=(const Str &str) = default; // non const operator.
+};
+
+// This class is non-trivially copyable because the copy-constructor and copy
+// assignment take non-const references.
+struct ModifiesRightSide {
+ ModifiesRightSide() = default;
+ ModifiesRightSide(ModifiesRightSide &) = default;
+ bool operator<(ModifiesRightSide &) const;
+ ModifiesRightSide &operator=(ModifiesRightSide &) = default;
+};
+
+template <typename T>
+void copyArg(T);
+
+template <typename T>
+void nonConstRefArg(T &);
+
+// If we define this as a template, the type is deduced to "T&",
+// and "const (T&) &" is the same as "T& &", and this collapses to "T&".
+void constRefArg(const Str &);
+void constRefArg(const ModifiesRightSide &);
+void constRefArg(const int &);
+
+void foo();
+
+const int N = 10;
+Str Array[N], OtherStr;
+ModifiesRightSide Right[N], OtherRight;
+int Ints[N], OtherInt;
+
+void memberFunctionsAndOperators() {
+ // Calling const member functions or operator is a const usage.
+ for (int I = 0; I < N; ++I) {
+ Array[I].constMember(0);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: I.constMember(0);
+
+ for (int I = 0; I < N; ++I) {
+ if (Array[I] < OtherStr)
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: if (I < OtherStr)
+ for (int I = 0; I < N; ++I) {
+ if (Right[I] < OtherRight)
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (const auto & I : Right)
+ // CHECK-FIXES-NEXT: if (I < OtherRight)
+
+ // Calling non-const member functions is not.
+ for (int I = 0; I < N; ++I) {
+ Array[I].nonConstMember(0);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Array)
+ // CHECK-FIXES-NEXT: I.nonConstMember(0);
+
+ for (int I = 0; I < N; ++I) {
+ Array[I] = OtherStr;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Array)
+ // CHECK-FIXES-NEXT: I = OtherStr;
+
+ for (int I = 0; I < N; ++I) {
+ Right[I] = OtherRight;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Right)
+ // CHECK-FIXES-NEXT: I = OtherRight;
+}
+
+void usedAsParameterToFunctionOrOperator() {
+ // Copying is OK, as long as the copy constructor takes a const-reference.
+ for (int I = 0; I < N; ++I) {
+ copyArg(Array[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: copyArg(I);
+
+ for (int I = 0; I < N; ++I) {
+ copyArg(Right[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Right)
+ // CHECK-FIXES-NEXT: copyArg(I);
+
+ // Using as a const reference argument is allowed.
+ for (int I = 0; I < N; ++I) {
+ constRefArg(Array[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: constRefArg(I);
+
+ for (int I = 0; I < N; ++I) {
+ if (OtherStr < Array[I])
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: if (OtherStr < I)
+
+ for (int I = 0; I < N; ++I) {
+ constRefArg(Right[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (const auto & I : Right)
+ // CHECK-FIXES-NEXT: constRefArg(I);
+
+ // Using as a non-const reference is not.
+ for (int I = 0; I < N; ++I) {
+ nonConstRefArg(Array[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Array)
+ // CHECK-FIXES-NEXT: nonConstRefArg(I);
+ for (int I = 0; I < N; ++I) {
+ nonConstRefArg(Right[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Right)
+ // CHECK-FIXES-NEXT: nonConstRefArg(I);
+ for (int I = 0; I < N; ++I) {
+ if (OtherRight < Right[I])
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Right)
+ // CHECK-FIXES-NEXT: if (OtherRight < I)
+}
+
+void primitiveTypes() {
+ // As argument to a function.
+ for (int I = 0; I < N; ++I) {
+ copyArg(Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: copyArg(Int);
+ for (int I = 0; I < N; ++I) {
+ constRefArg(Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: constRefArg(Int);
+ for (int I = 0; I < N; ++I) {
+ nonConstRefArg(Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & Int : Ints)
+ // CHECK-FIXES-NEXT: nonConstRefArg(Int);
+
+ // Builtin operators.
+ // Comparisons.
+ for (int I = 0; I < N; ++I) {
+ if (Ints[I] < N)
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: if (Int < N)
+
+ for (int I = 0; I < N; ++I) {
+ if (N == Ints[I])
+ foo();
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: if (N == Int)
+
+ // Assignment.
+ for (int I = 0; I < N; ++I) {
+ Ints[I] = OtherInt;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & Int : Ints)
+ // CHECK-FIXES-NEXT: Int = OtherInt;
+
+ for (int I = 0; I < N; ++I) {
+ OtherInt = Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: OtherInt = Int;
+
+ for (int I = 0; I < N; ++I) {
+ OtherInt = Ints[I] = OtherInt;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & Int : Ints)
+ // CHECK-FIXES-NEXT: OtherInt = Int = OtherInt;
+
+ // Arithmetic operations.
+ for (int I = 0; I < N; ++I) {
+ OtherInt += Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: OtherInt += Int;
+
+ for (int I = 0; I < N; ++I) {
+ Ints[I] += Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & Int : Ints)
+ // CHECK-FIXES-NEXT: Int += Int;
+
+ for (int I = 0; I < N; ++I) {
+ int Res = 5 * (Ints[I] + 1) - Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: int Res = 5 * (Int + 1) - Int;
+}
+
+void takingReferences() {
+ // We do it twice to prevent the check from thinking that they are aliases.
+
+ // Class type.
+ for (int I = 0; I < N; ++I) {
+ Str &J = Array[I];
+ Str &K = Array[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & I : Array)
+ // CHECK-FIXES-NEXT: Str &J = I;
+ // CHECK-FIXES-NEXT: Str &K = I;
+ for (int I = 0; I < N; ++I) {
+ const Str &J = Array[I];
+ const Str &K = Array[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+ // CHECK-FIXES-NEXT: const Str &J = I;
+ // CHECK-FIXES-NEXT: const Str &K = I;
+
+ // Primitive type.
+ for (int I = 0; I < N; ++I) {
+ int &J = Ints[I];
+ int &K = Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & Int : Ints)
+ // CHECK-FIXES-NEXT: int &J = Int;
+ // CHECK-FIXES-NEXT: int &K = Int;
+ for (int I = 0; I < N; ++I) {
+ const int &J = Ints[I];
+ const int &K = Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: const int &J = Int;
+ // CHECK-FIXES-NEXT: const int &K = Int;
+
+ // Aliases.
+ for (int I = 0; I < N; ++I) {
+ const Str &J = Array[I];
+ (void)J;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto J : Array)
+ for (int I = 0; I < N; ++I) {
+ Str &J = Array[I];
+ (void)J;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto & J : Array)
+
+ for (int I = 0; I < N; ++I) {
+ const int &J = Ints[I];
+ (void)J;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int J : Ints)
+
+ for (int I = 0; I < N; ++I) {
+ int &J = Ints[I];
+ (void)J;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int & J : Ints)
+}
+
+template <class T>
+struct vector {
+ unsigned size() const;
+ const T &operator[](int) const;
+ T &operator[](int);
+ T *begin();
+ T *end();
+ const T *begin() const;
+ const T *end() const;
+};
+
+// If the elements are already constant, we won't do any ImplicitCast to const.
+void testContainerOfConstIents() {
+ const int Ints[N]{};
+ for (int I = 0; I < N; ++I) {
+ OtherInt -= Ints[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+
+ vector<const Str> Strs;
+ for (int I = 0; I < Strs.size(); ++I) {
+ Strs[I].constMember(0);
+ constRefArg(Strs[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+ // CHECK-FIXES: for (auto Str : Strs)
+}
+
+// When we are inside a const-qualified member functions, all the data members
+// are implicitly set as const. As before, there won't be any ImplicitCast to
+// const in their usages.
+class TestInsideConstFunction {
+ const static int N = 10;
+ int Ints[N];
+ Str Array[N];
+ vector<int> V;
+
+ void foo() const {
+ for (int I = 0; I < N; ++I) {
+ if (Ints[I])
+ copyArg(Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+ // CHECK-FIXES: for (int Int : Ints)
+
+ for (int I = 0; I < N; ++I) {
+ Array[I].constMember(0);
+ constRefArg(Array[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+ // CHECK-FIXES: for (auto I : Array)
+
+ for (int I = 0; I < V.size(); ++I) {
+ if (V[I])
+ copyArg(V[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+ // CHECK-FIXES: for (int I : V)
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+namespace Dependency {
+
+void f() {
+ const int N = 6;
+ const int M = 8;
+ int Arr[N][M];
+
+ for (int I = 0; I < N; ++I) {
+ int A = 0;
+ int B = Arr[I][A];
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Arr)
+ // CHECK-FIXES-NEXT: int A = 0;
+ // CHECK-FIXES-NEXT: int B = I[A];
+
+ for (int J = 0; J < M; ++J) {
+ int A = 0;
+ int B = Arr[A][J];
+ }
+}
+
+} // namespace Dependency
+
+namespace NamingAlias {
+
+const int N = 10;
+
+Val Arr[N];
+dependent<Val> V;
+dependent<Val> *Pv;
+Val &func(Val &);
+void sideEffect(int);
+
+void aliasing() {
+ // If the loop container is only used for a declaration of a temporary
+ // variable to hold each element, we can name the new variable for the
+ // converted range-based loop as the temporary variable's name.
+
+ // In the following case, "T" is used as a temporary variable to hold each
+ // element, and thus we consider the name "T" aliased to the loop.
+ // The extra blank braces are left as a placeholder for after the variable
+ // declaration is deleted.
+ for (int I = 0; I < N; ++I) {
+ Val &T = Arr[I];
+ {}
+ int Y = T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & T : Arr)
+ // CHECK-FIXES-NOT: Val &{{[a-z_]+}} =
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: int Y = T.X;
+
+ // The container was not only used to initialize a temporary loop variable for
+ // the container's elements, so we do not alias the new loop variable.
+ for (int I = 0; I < N; ++I) {
+ Val &T = Arr[I];
+ int Y = T.X;
+ int Z = Arr[I].X + T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Arr)
+ // CHECK-FIXES-NEXT: Val &T = I;
+ // CHECK-FIXES-NEXT: int Y = T.X;
+ // CHECK-FIXES-NEXT: int Z = I.X + T.X;
+
+ for (int I = 0; I < N; ++I) {
+ Val T = Arr[I];
+ int Y = T.X;
+ int Z = Arr[I].X + T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Arr)
+ // CHECK-FIXES-NEXT: Val T = I;
+ // CHECK-FIXES-NEXT: int Y = T.X;
+ // CHECK-FIXES-NEXT: int Z = I.X + T.X;
+
+ // The same for pseudo-arrays like std::vector<T> (or here dependent<Val>)
+ // which provide a subscript operator[].
+ for (int I = 0; I < V.size(); ++I) {
+ Val &T = V[I];
+ {}
+ int Y = T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & T : V)
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: int Y = T.X;
+
+ // The same with a call to at()
+ for (int I = 0; I < Pv->size(); ++I) {
+ Val &T = Pv->at(I);
+ {}
+ int Y = T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & T : *Pv)
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: int Y = T.X;
+
+ for (int I = 0; I < N; ++I) {
+ Val &T = func(Arr[I]);
+ int Y = T.X;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Arr)
+ // CHECK-FIXES-NEXT: Val &T = func(I);
+ // CHECK-FIXES-NEXT: int Y = T.X;
+
+ int IntArr[N];
+ for (unsigned I = 0; I < N; ++I) {
+ if (int Alias = IntArr[I]) {
+ sideEffect(Alias);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Alias : IntArr)
+ // CHECK-FIXES-NEXT: if (Alias)
+
+ for (unsigned I = 0; I < N; ++I) {
+ while (int Alias = IntArr[I]) {
+ sideEffect(Alias);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Alias : IntArr)
+ // CHECK-FIXES-NEXT: while (Alias)
+
+ for (unsigned I = 0; I < N; ++I) {
+ switch (int Alias = IntArr[I]) {
+ default:
+ sideEffect(Alias);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Alias : IntArr)
+ // CHECK-FIXES-NEXT: switch (Alias)
+
+ for (unsigned I = 0; I < N; ++I) {
+ for (int Alias = IntArr[I]; Alias < N; ++Alias) {
+ sideEffect(Alias);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Alias : IntArr)
+ // CHECK-FIXES-NEXT: for (; Alias < N; ++Alias)
+
+ for (unsigned I = 0; I < N; ++I) {
+ for (unsigned J = 0; int Alias = IntArr[I]; ++J) {
+ sideEffect(Alias);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Alias : IntArr)
+ // CHECK-FIXES-NEXT: for (unsigned J = 0; Alias; ++J)
+
+ struct IntRef { IntRef(); IntRef(const int& i); operator int*(); };
+ for (int I = 0; I < N; ++I) {
+ IntRef Int(IntArr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : IntArr)
+ // CHECK-FIXES-NEXT: IntRef Int(I);
+
+ int *PtrArr[N];
+ for (unsigned I = 0; I < N; ++I) {
+ const int* const P = PtrArr[I];
+ printf("%d\n", *P);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto P : PtrArr)
+ // CHECK-FIXES-NEXT: printf("%d\n", *P);
+
+ IntRef Refs[N];
+ for (unsigned I = 0; I < N; ++I) {
+ int *P = Refs[I];
+ printf("%d\n", *P);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Ref : Refs)
+ // CHECK-FIXES-NEXT: int *P = Ref;
+ // CHECK-FIXES-NEXT: printf("%d\n", *P);
+
+ // Ensure that removing the alias doesn't leave empty lines behind.
+ for (int I = 0; I < N; ++I) {
+ auto &X = IntArr[I];
+ X = 0;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & X : IntArr) {
+ // CHECK-FIXES-NEXT: {{^ X = 0;$}}
+ // CHECK-FIXES-NEXT: {{^ }$}}
+}
+
+void refs_and_vals() {
+ // The following tests check that the transform correctly preserves the
+ // reference or value qualifiers of the aliased variable. That is, if the
+ // variable was declared as a value, the loop variable will be declared as a
+ // value and vice versa for references.
+
+ S Ss;
+ const S S_const = Ss;
+
+ for (S::const_iterator It = S_const.begin(); It != S_const.end(); ++It) {
+ MutableVal Alias = *It;
+ {}
+ Alias.X = 0;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto Alias : S_const)
+ // CHECK-FIXES-NOT: MutableVal {{[a-z_]+}} =
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: Alias.X = 0;
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ MutableVal Alias = *It;
+ {}
+ Alias.X = 0;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto Alias : Ss)
+ // CHECK-FIXES-NOT: MutableVal {{[a-z_]+}} =
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: Alias.X = 0;
+
+ for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+ MutableVal &Alias = *It;
+ {}
+ Alias.X = 0;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Alias : Ss)
+ // CHECK-FIXES-NOT: MutableVal &{{[a-z_]+}} =
+ // CHECK-FIXES-NEXT: {}
+ // CHECK-FIXES-NEXT: Alias.X = 0;
+
+ dependent<int> Dep, Other;
+ for (dependent<int>::iterator It = Dep.begin(), E = Dep.end(); It != E; ++It) {
+ printf("%d\n", *It);
+ const int& Idx = Other[0];
+ unsigned Othersize = Other.size();
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : Dep)
+ // CHECK-FIXES-NEXT: printf("%d\n", It);
+ // CHECK-FIXES-NEXT: const int& Idx = Other[0];
+ // CHECK-FIXES-NEXT: unsigned Othersize = Other.size();
+
+ for (int i = 0; i < Other.size(); ++i) {
+ Other.at(i);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & i : Other)
+ // CHECK-FIXES: i;
+
+ for (int I = 0, E = Dep.size(); I != E; ++I) {
+ int Idx = Other.at(I);
+ Other.at(I, I); // Should not trigger assert failure.
+ }
+}
+
+struct MemberNaming {
+ const static int N = 10;
+ int Ints[N], Ints_[N];
+ dependent<int> DInts;
+ void loops() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", Ints_[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Int : Ints_)
+ // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+ for (int I = 0; I < DInts.size(); ++I) {
+ printf("%d\n", DInts[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int DInt : DInts)
+ // CHECK-FIXES-NEXT: printf("%d\n", DInt);
+ }
+
+ void outOfLine();
+};
+void MemberNaming::outOfLine() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", Ints[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Int : Ints)
+ // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", Ints_[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Int : Ints_)
+ // CHECK-FIXES-NEXT: printf("%d\n", Int);
+}
+
+} // namespace NamingAlias
+
+namespace NamingConlict {
+
+#define MAX(a, b) (a > b) ? a : b
+#define DEF 5
+
+const int N = 10;
+int Nums[N];
+int Sum = 0;
+
+namespace ns {
+struct St {
+ int X;
+};
+}
+
+void sameNames() {
+ int Num = 0;
+ for (int I = 0; I < N; ++I) {
+ printf("Fibonacci number is %d\n", Nums[I]);
+ Sum += Nums[I] + 2 + Num;
+ (void)Nums[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Nums)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2 + Num;
+ // CHECK-FIXES-NEXT: (void)I;
+
+ int Elem = 0;
+ for (int I = 0; I < N; ++I) {
+ printf("Fibonacci number is %d\n", Nums[I]);
+ Sum += Nums[I] + 2 + Num + Elem;
+ (void)Nums[I];
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Nums)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+ // CHECK-FIXES-NEXT: Sum += I + 2 + Num + Elem;
+ // CHECK-FIXES-NEXT: (void)I;
+}
+
+void oldIndexConflict() {
+ for (int Num = 0; Num < N; ++Num) {
+ printf("Num: %d\n", Nums[Num]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int Num : Nums)
+ // CHECK-FIXES-NEXT: printf("Num: %d\n", Num);
+
+ S Things;
+ for (S::iterator Thing = Things.begin(), End = Things.end(); Thing != End; ++Thing) {
+ printf("Thing: %d %d\n", Thing->X, (*Thing).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Thing : Things)
+ // CHECK-FIXES-NEXT: printf("Thing: %d %d\n", Thing.X, Thing.X);
+}
+
+void macroConflict() {
+ S MAXs;
+ for (S::iterator It = MAXs.begin(), E = MAXs.end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ printf("Max of 3 and 5: %d\n", MAX(3, 5));
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : MAXs)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+ // CHECK-FIXES-NEXT: printf("Max of 3 and 5: %d\n", MAX(3, 5));
+
+ for (S::const_iterator It = MAXs.begin(), E = MAXs.end(); It != E; ++It) {
+ printf("s has value %d\n", (*It).X);
+ printf("Max of 3 and 5: %d\n", MAX(3, 5));
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto It : MAXs)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+ // CHECK-FIXES-NEXT: printf("Max of 3 and 5: %d\n", MAX(3, 5));
+
+ T DEFs;
+ for (T::iterator It = DEFs.begin(), E = DEFs.end(); It != E; ++It) {
+ if (*It == DEF) {
+ printf("I found %d\n", *It);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : DEFs)
+ // CHECK-FIXES-NEXT: if (It == DEF)
+ // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+}
+
+void keywordConflict() {
+ T ints;
+ for (T::iterator It = ints.begin(), E = ints.end(); It != E; ++It) {
+ *It = 5;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : ints)
+ // CHECK-FIXES-NEXT: It = 5;
+
+ U __FUNCTION__s;
+ for (U::iterator It = __FUNCTION__s.begin(), E = __FUNCTION__s.end();
+ It != E; ++It) {
+ int __FUNCTION__s_It = (*It).X + 2;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : __FUNCTION__s)
+ // CHECK-FIXES-NEXT: int __FUNCTION__s_It = It.X + 2;
+}
+
+void typeConflict() {
+ T Vals;
+ // Using the name "Val", although it is the name of an existing struct, is
+ // safe in this loop since it will only exist within this scope.
+ for (T::iterator It = Vals.begin(), E = Vals.end(); It != E; ++It)
+ (void) *It;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & Val : Vals)
+
+ // We cannot use the name "Val" in this loop since there is a reference to
+ // it in the body of the loop.
+ for (T::iterator It = Vals.begin(), E = Vals.end(); It != E; ++It) {
+ *It = sizeof(Val);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : Vals)
+ // CHECK-FIXES-NEXT: It = sizeof(Val);
+
+ typedef struct Val TD;
+ U TDs;
+ // Naming the variable "TD" within this loop is safe because the typedef
+ // was never used within the loop.
+ for (U::iterator It = TDs.begin(), E = TDs.end(); It != E; ++It)
+ (void) *It;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & TD : TDs)
+
+ // "TD" cannot be used in this loop since the typedef is being used.
+ for (U::iterator It = TDs.begin(), E = TDs.end(); It != E; ++It) {
+ TD V;
+ V.X = 5;
+ (void) *It;
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : TDs)
+ // CHECK-FIXES-NEXT: TD V;
+ // CHECK-FIXES-NEXT: V.X = 5;
+
+ using ns::St;
+ T Sts;
+ for (T::iterator It = Sts.begin(), E = Sts.end(); It != E; ++It) {
+ *It = sizeof(St);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : Sts)
+ // CHECK-FIXES-NEXT: It = sizeof(St);
+}
+
+} // namespace NamingConflict
+
+namespace FreeBeginEnd {
+
+// FIXME: Loop Convert should detect free begin()/end() functions.
+
+struct MyArray {
+ unsigned size();
+};
+
+template <typename T>
+struct MyContainer {
+};
+
+int *begin(const MyArray &Arr);
+int *end(const MyArray &Arr);
+
+template <typename T>
+T *begin(const MyContainer<T> &C);
+template <typename T>
+T *end(const MyContainer<T> &C);
+
+// The Loop Convert Transform doesn't detect free functions begin()/end() and
+// so fails to transform these cases which it should.
+void f() {
+ MyArray Arr;
+ for (unsigned I = 0, E = Arr.size(); I < E; ++I) {
+ }
+
+ MyContainer<int> C;
+ for (int *I = begin(C), *E = end(C); I != E; ++I) {
+ }
+}
+
+} // namespace FreeBeginEnd
+
+namespace Nesting {
+
+void g(S::iterator It);
+void const_g(S::const_iterator It);
+class Foo {
+ public:
+ void g(S::iterator It);
+ void const_g(S::const_iterator It);
+};
+
+void f() {
+ const int N = 10;
+ const int M = 15;
+ Val Arr[N];
+ for (int I = 0; I < N; ++I) {
+ for (int J = 0; J < N; ++J) {
+ int K = Arr[I].X + Arr[J].X;
+ // The repeat is there to allow FileCheck to make sure the two variable
+ // names aren't the same.
+ int L = Arr[I].X + Arr[J].X;
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-8]]:3: warning: use range-based for loop instead
+ // CHECK-MESSAGES: :[[@LINE-8]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Arr)
+ // CHECK-FIXES-NEXT: for (auto & J : Arr)
+ // CHECK-FIXES-NEXT: int K = I.X + J.X;
+ // CHECK-FIXES-NOT: int L = I.X + I.X;
+
+ // The inner loop is also convertible, but doesn't need to be converted
+ // immediately. FIXME: update this test when that changes.
+ Val Nest[N][M];
+ for (int I = 0; I < N; ++I) {
+ for (int J = 0; J < M; ++J) {
+ printf("Got item %d", Nest[I][J].X);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Nest)
+ // CHECK-FIXES-NEXT: for (int J = 0; J < M; ++J)
+ // CHECK-FIXES-NEXT: printf("Got item %d", I[J].X);
+
+ // Note that the order of M and N are switched for this test.
+ for (int J = 0; J < M; ++J) {
+ for (int I = 0; I < N; ++I) {
+ printf("Got item %d", Nest[I][J].X);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop instead
+ // CHECK-FIXES-NOT: for (auto & {{[a-zA-Z_]+}} : Nest[I])
+ // CHECK-FIXES: for (int J = 0; J < M; ++J)
+ // CHECK-FIXES-NEXT: for (auto & I : Nest)
+ // CHECK-FIXES-NEXT: printf("Got item %d", I[J].X);
+
+ // The inner loop is also convertible.
+ Nested<T> NestT;
+ for (Nested<T>::iterator I = NestT.begin(), E = NestT.end(); I != E; ++I) {
+ for (T::iterator TI = (*I).begin(), TE = (*I).end(); TI != TE; ++TI) {
+ printf("%d", *TI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : NestT)
+ // CHECK-FIXES-NEXT: for (T::iterator TI = I.begin(), TE = I.end(); TI != TE; ++TI)
+ // CHECK-FIXES-NEXT: printf("%d", *TI);
+
+ // The inner loop is also convertible.
+ Nested<S> NestS;
+ for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+ for (S::const_iterator SI = (*I).begin(), SE = (*I).end(); SI != SE; ++SI) {
+ printf("%d", *SI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto I : NestS)
+ // CHECK-FIXES-NEXT: for (S::const_iterator SI = I.begin(), SE = I.end(); SI != SE; ++SI)
+ // CHECK-FIXES-NEXT: printf("%d", *SI);
+
+ for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+ const S &Ss = *I;
+ for (S::const_iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+ printf("%d", *SI);
+ const_g(SI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto Ss : NestS)
+
+ for (Nested<S>::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+ S &Ss = *I;
+ for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+ printf("%d", *SI);
+ g(SI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Ss : NestS)
+
+ Foo foo;
+ for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+ const S &Ss = *I;
+ for (S::const_iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+ printf("%d", *SI);
+ foo.const_g(SI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto Ss : NestS)
+
+ for (Nested<S>::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+ S &Ss = *I;
+ for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+ printf("%d", *SI);
+ foo.g(SI);
+ }
+ }
+ // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & Ss : NestS)
+
+}
+
+} // namespace Nesting
+
+namespace SingleIterator {
+
+void complexContainer() {
+ X Exes[5];
+ int Index = 0;
+
+ for (S::iterator I = Exes[Index].getS().begin(), E = Exes[Index].getS().end(); I != E; ++I) {
+ MutableVal K = *I;
+ MutableVal J = *I;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Exes[Index].getS())
+ // CHECK-FIXES-NEXT: MutableVal K = I;
+ // CHECK-FIXES-NEXT: MutableVal J = I;
+}
+
+void f() {
+ /// begin()/end() - based for loops here:
+ T Tt;
+ for (T::iterator It = Tt.begin(); It != Tt.end(); ++It) {
+ printf("I found %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : Tt)
+ // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+ T *Pt;
+ for (T::iterator It = Pt->begin(); It != Pt->end(); ++It) {
+ printf("I found %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : *Pt)
+ // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+ S Ss;
+ for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ S *Ps;
+ for (S::iterator It = Ps->begin(); It != Ps->end(); ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & P : *Ps)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+ for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ printf("s has value %d\n", It->X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ It->X = 3;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.X = 3;
+
+ for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ (*It).X = 3;
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.X = 3;
+
+ for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ It->nonConstFun(4, 5);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Ss)
+ // CHECK-FIXES-NEXT: It.nonConstFun(4, 5);
+
+ U Uu;
+ for (U::iterator It = Uu.begin(); It != Uu.end(); ++It) {
+ printf("s has value %d\n", It->X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Uu)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ for (U::iterator It = Uu.begin(); It != Uu.end(); ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : Uu)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ U::iterator A;
+ for (U::iterator I = Uu.begin(); I != Uu.end(); ++I)
+ int K = A->X + I->X;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & I : Uu)
+ // CHECK-FIXES-NEXT: int K = A->X + I.X;
+
+ dependent<int> V;
+ for (dependent<int>::iterator It = V.begin();
+ It != V.end(); ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+ for (dependent<int>::iterator It(V.begin());
+ It != V.end(); ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & It : V)
+ // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+ doublyDependent<int, int> intmap;
+ for (doublyDependent<int, int>::iterator It = intmap.begin();
+ It != intmap.end(); ++It) {
+ printf("intmap[%d] = %d", It->first, It->second);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & It : intmap)
+ // CHECK-FIXES-NEXT: printf("intmap[%d] = %d", It.first, It.second);
+}
+
+void different_type() {
+ // Tests to verify the proper use of auto where the init variable type and the
+ // initializer type differ or are mostly the same except for const qualifiers.
+
+ // Ss.begin() returns a type 'iterator' which is just a non-const pointer and
+ // differs from const_iterator only on the const qualification.
+ S Ss;
+ for (S::const_iterator It = Ss.begin(); It != Ss.end(); ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto It : Ss)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+ S *Ps;
+ for (S::const_iterator It = Ps->begin(); It != Ps->end(); ++It) {
+ printf("s has value %d\n", (*It).X);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto P : *Ps)
+ // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+ // V.begin() returns a user-defined type 'iterator' which, since it's
+ // different from const_iterator, disqualifies these loops from
+ // transformation.
+ dependent<int> V;
+ for (dependent<int>::const_iterator It = V.begin(); It != V.end(); ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+
+ for (dependent<int>::const_iterator It(V.begin()); It != V.end(); ++It) {
+ printf("Fibonacci number is %d\n", *It);
+ }
+}
+
+} // namespace SingleIterator
+
+
+namespace Macros {
+
+#define TWO_PARAM(x, y) if (x == y) {}
+#define THREE_PARAM(x, y, z) if (x == y) {z;}
+
+const int N = 10;
+int Arr[N];
+
+void messing_with_macros() {
+ for (int I = 0; I < N; ++I) {
+ printf("Value: %d\n", Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: printf("Value: %d\n", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("Value: %d\n", CONT Arr[I]);
+ }
+
+ // Multiple macro arguments.
+ for (int I = 0; I < N; ++I) {
+ TWO_PARAM(Arr[I], Arr[I]);
+ THREE_PARAM(Arr[I], Arr[I], Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: TWO_PARAM(I, I);
+ // CHECK-FIXES-NEXT: THREE_PARAM(I, I, I);
+}
+
+} // namespace Macros
+
+namespace Templates {
+
+template <class Container>
+void set_union(Container &container) {
+ for (typename Container::const_iterator SI = container.begin(),
+ SE = container.end(); SI != SE; ++SI) {
+ (void) *SI;
+ }
+
+ S Ss;
+ for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI)
+ (void) *SI;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (auto & SI : Ss)
+}
+
+void template_instantiation() {
+ S Ss;
+ set_union(Ss);
+}
+
+} // namespace Templates
+
+namespace Lambdas {
+
+void capturesIndex() {
+ const int N = 10;
+ int Arr[N];
+ // FIXME: the next four loops could be convertible, if the capture list is
+ // also changed.
+
+ for (int I = 0; I < N; ++I)
+ auto F1 = [Arr, I]() { int R1 = Arr[I] + 1; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto F1 = [Arr, &I]() { int R1 = I + 1; };
+
+ for (int I = 0; I < N; ++I)
+ auto F2 = [Arr, &I]() { int R2 = Arr[I] + 3; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto F2 = [Arr, &I]() { int R2 = I + 3; };
+
+ // FIXME: alias don't work if the index is captured.
+ // Alias declared inside lambda (by value).
+ for (int I = 0; I < N; ++I)
+ auto F3 = [&Arr, I]() { int R3 = Arr[I]; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto F3 = [&Arr, &I]() { int R3 = I; };
+
+
+ for (int I = 0; I < N; ++I)
+ auto F4 = [&Arr, &I]() { int R4 = Arr[I]; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto F4 = [&Arr, &I]() { int R4 = I; };
+
+ // Alias declared inside lambda (by reference).
+ for (int I = 0; I < N; ++I)
+ auto F5 = [&Arr, I]() { int &R5 = Arr[I]; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: auto F5 = [&Arr, &I]() { int &R5 = I; };
+
+
+ for (int I = 0; I < N; ++I)
+ auto F6 = [&Arr, &I]() { int &R6 = Arr[I]; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Arr)
+ // CHECK-FIXES-NEXT: auto F6 = [&Arr, &I]() { int &R6 = I; };
+
+ for (int I = 0; I < N; ++I) {
+ auto F = [Arr, I](int k) {
+ printf("%d\n", Arr[I] + k);
+ };
+ F(Arr[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto F = [Arr, &I](int k)
+ // CHECK-FIXES-NEXT: printf("%d\n", I + k);
+ // CHECK-FIXES: F(I);
+}
+
+void implicitCapture() {
+ const int N = 10;
+ int Arr[N];
+ // Index is used, not convertible.
+ for (int I = 0; I < N; ++I) {
+ auto G1 = [&]() {
+ int R = Arr[I];
+ int J = I;
+ };
+ }
+
+ for (int I = 0; I < N; ++I) {
+ auto G2 = [=]() {
+ int R = Arr[I];
+ int J = I;
+ };
+ }
+
+ // Convertible.
+ for (int I = 0; I < N; ++I) {
+ auto G3 = [&]() {
+ int R3 = Arr[I];
+ int J3 = Arr[I] + R3;
+ };
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto G3 = [&]()
+ // CHECK-FIXES-NEXT: int R3 = I;
+ // CHECK-FIXES-NEXT: int J3 = I + R3;
+
+ for (int I = 0; I < N; ++I) {
+ auto G4 = [=]() {
+ int R4 = Arr[I] + 5;
+ };
+ }
+ // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto G4 = [=]()
+ // CHECK-FIXES-NEXT: int R4 = I + 5;
+
+ // Alias by value.
+ for (int I = 0; I < N; ++I) {
+ auto G5 = [&]() {
+ int R5 = Arr[I];
+ int J5 = 8 + R5;
+ };
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int R5 : Arr)
+ // CHECK-FIXES-NEXT: auto G5 = [&]()
+ // CHECK-FIXES-NEXT: int J5 = 8 + R5;
+
+ // Alias by reference.
+ for (int I = 0; I < N; ++I) {
+ auto G6 = [&]() {
+ int &R6 = Arr[I];
+ int J6 = -1 + R6;
+ };
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & R6 : Arr)
+ // CHECK-FIXES-NEXT: auto G6 = [&]()
+ // CHECK-FIXES-NEXT: int J6 = -1 + R6;
+}
+
+void iterators() {
+ dependent<int> Dep;
+
+ for (dependent<int>::iterator I = Dep.begin(), E = Dep.end(); I != E; ++I)
+ auto H1 = [&I]() { int R = *I; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Dep)
+ // CHECK-FIXES-NEXT: auto H1 = [&I]() { int R = I; };
+
+ for (dependent<int>::iterator I = Dep.begin(), E = Dep.end(); I != E; ++I)
+ auto H2 = [&]() { int R = *I + 2; };
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int & I : Dep)
+ // CHECK-FIXES-NEXT: auto H2 = [&]() { int R = I + 2; };
+
+ // FIXME: It doesn't work with const iterators.
+ for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+ I != E; ++I)
+ auto H3 = [I]() { int R = *I; };
+
+ for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+ I != E; ++I)
+ auto H4 = [&]() { int R = *I + 1; };
+
+ for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+ I != E; ++I)
+ auto H5 = [=]() { int R = *I; };
+}
+
+void captureByValue() {
+ // When the index is captured by value, we should replace this by a capture
+ // by reference. This avoids extra copies.
+ // FIXME: this could change semantics on array or pseudoarray loops if the
+ // container is captured by copy.
+ const int N = 10;
+ int Arr[N];
+ dependent<int> Dep;
+
+ for (int I = 0; I < N; ++I) {
+ auto C1 = [&Arr, I]() { if (Arr[I] == 1); };
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Arr)
+ // CHECK-FIXES-NEXT: auto C1 = [&Arr, &I]() { if (I == 1); };
+
+ for (unsigned I = 0; I < Dep.size(); ++I) {
+ auto C2 = [&Dep, I]() { if (Dep[I] == 2); };
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Dep)
+ // CHECK-FIXES-NEXT: auto C2 = [&Dep, &I]() { if (I == 2); };
+}
+
+} // namespace Lambdas
+
+namespace InitLists {
+
+struct D { int Ii; };
+struct E { D Dd; };
+int g(int B);
+
+void f() {
+ const unsigned N = 3;
+ int Array[N];
+
+ // Subtrees of InitListExpr are visited twice. Test that we do not do repeated
+ // replacements.
+ for (unsigned I = 0; I < N; ++I) {
+ int A{ Array[I] };
+ int B{ g(Array[I]) };
+ int C{ g( { Array[I] } ) };
+ D Dd{ { g( { Array[I] } ) } };
+ E Ee{ { { g( { Array[I] } ) } } };
+ }
+ // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : Array)
+ // CHECK-FIXES-NEXT: int A{ I };
+ // CHECK-FIXES-NEXT: int B{ g(I) };
+ // CHECK-FIXES-NEXT: int C{ g( { I } ) };
+ // CHECK-FIXES-NEXT: D Dd{ { g( { I } ) } };
+ // CHECK-FIXES-NEXT: E Ee{ { { g( { I } ) } } };
+}
+
+} // namespace InitLists
+
+void bug28341() {
+ char v[5];
+ for(int i = 0; i < 5; ++i) {
+ unsigned char value = v[i];
+ if (value > 127)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for(unsigned char value : v)
+ // CHECK-FIXES-NEXT: if (value > 127)
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'lower_case'}]}" \
+// RUN: -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int n = 10;
+int arr[n];
+int nums[n];
+int nums_[n];
+
+void naming() {
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", arr[i]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+ // CHECK-FIXES: for (int i : arr)
+ // CHECK-FIXES-NEXT: printf("%d\n", i);
+
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", nums[i]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int num : nums)
+ // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", nums_[i]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int num : nums_)
+ // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+ int num = 0;
+ for (int i = 0; i < n; ++i) {
+ printf("%d\n", nums[i] + num);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int i : nums)
+ // CHECK-FIXES-NEXT: printf("%d\n", i + num);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+// CHECK-FIXES-NOT: for ({{.*[^:]:[^:].*}})
+// CHECK-MESSAGES-NOT: modernize-loop-convert
+
+namespace Negative {
+
+const int N = 6;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+int (*pArr)[N] = &Arr;
+int Sum = 0;
+
+// Checks for the Index start and end:
+void IndexStartAndEnd() {
+ for (int I = 0; I < N + 1; ++I)
+ Sum += Arr[I];
+
+ for (int I = 0; I < N - 1; ++I)
+ Sum += Arr[I];
+
+ for (int I = 1; I < N; ++I)
+ Sum += Arr[I];
+
+ for (int I = 1; I < N; ++I)
+ Sum += Arr[I];
+
+ for (int I = 0;; ++I)
+ Sum += (*pArr)[I];
+}
+
+// Checks for invalid increment steps:
+void increment() {
+ for (int I = 0; I < N; --I)
+ Sum += Arr[I];
+
+ for (int I = 0; I < N; I)
+ Sum += Arr[I];
+
+ for (int I = 0; I < N;)
+ Sum += Arr[I];
+
+ for (int I = 0; I < N; I += 2)
+ Sum++;
+}
+
+// Checks to make sure that the Index isn't used outside of the array:
+void IndexUse() {
+ for (int I = 0; I < N; ++I)
+ Arr[I] += 1 + I;
+}
+
+// Check for loops that don't mention arrays
+void noArray() {
+ for (int I = 0; I < N; ++I)
+ Sum += I;
+
+ for (int I = 0; I < N; ++I) {
+ }
+
+ for (int I = 0; I < N; ++I)
+ ;
+}
+
+// Checks for incorrect loop variables.
+void mixedVariables() {
+ int BadIndex;
+ for (int I = 0; BadIndex < N; ++I)
+ Sum += Arr[I];
+
+ for (int I = 0; I < N; ++BadIndex)
+ Sum += Arr[I];
+
+ for (int I = 0; BadIndex < N; ++BadIndex)
+ Sum += Arr[I];
+
+ for (int I = 0; BadIndex < N; ++BadIndex)
+ Sum += Arr[BadIndex];
+}
+
+// Checks for multiple arrays Indexed.
+void multipleArrays() {
+ int BadArr[N];
+
+ for (int I = 0; I < N; ++I)
+ Sum += Arr[I] + BadArr[I];
+
+ for (int I = 0; I < N; ++I) {
+ int K = BadArr[I];
+ Sum += Arr[I] + K;
+ }
+}
+
+}
+
+namespace NegativeIterator {
+
+S Ss;
+T Tt;
+U Tu;
+
+struct BadBeginEnd : T {
+ iterator notBegin();
+ iterator notEnd();
+};
+
+void notBeginOrEnd() {
+ BadBeginEnd Bad;
+ for (T::iterator I = Bad.notBegin(), E = Bad.end(); I != E; ++I)
+ int K = *I;
+
+ for (T::iterator I = Bad.begin(), E = Bad.notEnd(); I != E; ++I)
+ int K = *I;
+}
+
+void badLoopShapes() {
+ for (T::iterator I = Tt.begin(), E = Tt.end(), F = E; I != E; ++I)
+ int K = *I;
+
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E;)
+ int K = *I;
+
+ for (T::iterator I = Tt.begin(), E = Tt.end();; ++I)
+ int K = *I;
+
+ T::iterator OutsideI;
+ T::iterator OutsideE;
+
+ for (; OutsideI != OutsideE; ++OutsideI)
+ int K = *OutsideI;
+}
+
+void iteratorArrayMix() {
+ int Lower;
+ const int N = 6;
+ for (T::iterator I = Tt.begin(), E = Tt.end(); Lower < N; ++I)
+ int K = *I;
+
+ for (T::iterator I = Tt.begin(), E = Tt.end(); Lower < N; ++Lower)
+ int K = *I;
+}
+
+struct ExtraConstructor : T::iterator {
+ ExtraConstructor(T::iterator, int);
+ explicit ExtraConstructor(T::iterator);
+};
+
+void badConstructor() {
+ for (T::iterator I = ExtraConstructor(Tt.begin(), 0), E = Tt.end();
+ I != E; ++I)
+ int K = *I;
+ for (T::iterator I = ExtraConstructor(Tt.begin()), E = Tt.end(); I != E; ++I)
+ int K = *I;
+}
+
+void foo(S::iterator It) {}
+class Foo {public: void bar(S::iterator It); };
+Foo Fo;
+
+void iteratorUsed() {
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ foo(I);
+
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ Fo.bar(I);
+
+ S::iterator Ret;
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ Ret = I;
+}
+
+void iteratorMemberUsed() {
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ I.X = *I;
+
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ int K = I.X + *I;
+
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ int K = E.X + *I;
+}
+
+void iteratorMethodCalled() {
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ I.insert(3);
+
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ if (I != I)
+ int K = 3;
+}
+
+void iteratorOperatorCalled() {
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ int K = *(++I);
+
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ MutableVal K = *(++I);
+}
+
+void differentContainers() {
+ T Other;
+ for (T::iterator I = Tt.begin(), E = Other.end(); I != E; ++I)
+ int K = *I;
+
+ for (T::iterator I = Other.begin(), E = Tt.end(); I != E; ++I)
+ int K = *I;
+
+ S OtherS;
+ for (S::iterator I = Ss.begin(), E = OtherS.end(); I != E; ++I)
+ MutableVal K = *I;
+
+ for (S::iterator I = OtherS.begin(), E = Ss.end(); I != E; ++I)
+ MutableVal K = *I;
+}
+
+void wrongIterators() {
+ T::iterator Other;
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != Other; ++I)
+ int K = *I;
+}
+
+struct EvilArrow : U {
+ // Please, no one ever write code like this.
+ U *operator->();
+};
+
+void differentMemberAccessTypes() {
+ EvilArrow A;
+ for (EvilArrow::iterator I = A.begin(), E = A->end(); I != E; ++I)
+ Val K = *I;
+ for (EvilArrow::iterator I = A->begin(), E = A.end(); I != E; ++I)
+ Val K = *I;
+}
+
+void f(const T::iterator &It, int);
+void f(const T &It, int);
+void g(T &It, int);
+
+void iteratorPassedToFunction() {
+ for (T::iterator I = Tt.begin(), E = Tt.end(); I != E; ++I)
+ f(I, *I);
+}
+
+// FIXME: These tests can be removed if this tool ever does enough analysis to
+// decide that this is a safe transformation. Until then, we don't want it
+// applied.
+void iteratorDefinedOutside() {
+ T::iterator TheEnd = Tt.end();
+ for (T::iterator I = Tt.begin(); I != TheEnd; ++I)
+ int K = *I;
+
+ T::iterator TheBegin = Tt.begin();
+ for (T::iterator E = Tt.end(); TheBegin != E; ++TheBegin)
+ int K = *TheBegin;
+}
+
+} // namespace NegativeIterator
+
+namespace NegativePseudoArray {
+
+const int N = 6;
+dependent<int> V;
+dependent<int> *Pv;
+
+int Sum = 0;
+
+// Checks for the Index start and end:
+void IndexStartAndEnd() {
+ for (int I = 0; I < V.size() + 1; ++I)
+ Sum += V[I];
+
+ for (int I = 0; I < V.size() - 1; ++I)
+ Sum += V[I];
+
+ for (int I = 1; I < V.size(); ++I)
+ Sum += V[I];
+
+ for (int I = 1; I < V.size(); ++I)
+ Sum += V[I];
+
+ for (int I = 0;; ++I)
+ Sum += (*Pv)[I];
+}
+
+// Checks for invalid increment steps:
+void increment() {
+ for (int I = 0; I < V.size(); --I)
+ Sum += V[I];
+
+ for (int I = 0; I < V.size(); I)
+ Sum += V[I];
+
+ for (int I = 0; I < V.size();)
+ Sum += V[I];
+
+ for (int I = 0; I < V.size(); I += 2)
+ Sum++;
+}
+
+// Checks to make sure that the Index isn't used outside of the container:
+void IndexUse() {
+ for (int I = 0; I < V.size(); ++I)
+ V[I] += 1 + I;
+}
+
+// Checks for incorrect loop variables.
+void mixedVariables() {
+ int BadIndex;
+ for (int I = 0; BadIndex < V.size(); ++I)
+ Sum += V[I];
+
+ for (int I = 0; I < V.size(); ++BadIndex)
+ Sum += V[I];
+
+ for (int I = 0; BadIndex < V.size(); ++BadIndex)
+ Sum += V[I];
+
+ for (int I = 0; BadIndex < V.size(); ++BadIndex)
+ Sum += V[BadIndex];
+}
+
+// Checks for an array Indexed in addition to the container.
+void multipleArrays() {
+ int BadArr[N];
+
+ for (int I = 0; I < V.size(); ++I)
+ Sum += V[I] + BadArr[I];
+
+ for (int I = 0; I < V.size(); ++I)
+ Sum += BadArr[I];
+
+ for (int I = 0; I < V.size(); ++I) {
+ int K = BadArr[I];
+ Sum += K + 2;
+ }
+
+ for (int I = 0; I < V.size(); ++I) {
+ int K = BadArr[I];
+ Sum += V[I] + K;
+ }
+}
+
+// Checks for multiple containers being Indexed container.
+void multipleContainers() {
+ dependent<int> BadArr;
+
+ for (int I = 0; I < V.size(); ++I)
+ Sum += V[I] + BadArr[I];
+
+ for (int I = 0; I < V.size(); ++I)
+ Sum += BadArr[I];
+
+ for (int I = 0; I < V.size(); ++I) {
+ int K = BadArr[I];
+ Sum += K + 2;
+ }
+
+ for (int I = 0; I < V.size(); ++I) {
+ int K = BadArr[I];
+ Sum += V[I] + K;
+ }
+}
+
+// Check to make sure that dereferenced pointers-to-containers behave nicely.
+void derefContainer() {
+ // Note the dependent<T>::operator*() returns another dependent<T>.
+ // This test makes sure that we don't allow an arbitrary number of *'s.
+ for (int I = 0; I < Pv->size(); ++I)
+ Sum += (**Pv).at(I);
+
+ for (int I = 0; I < Pv->size(); ++I)
+ Sum += (**Pv)[I];
+}
+
+void wrongEnd() {
+ int Bad;
+ for (int I = 0, E = V.size(); I < Bad; ++I)
+ Sum += V[I];
+}
+
+// Checks to see that non-const member functions are not called on the container
+// object.
+// These could be conceivably allowed with a lower required confidence level.
+void memberFunctionCalled() {
+ for (int I = 0; I < V.size(); ++I) {
+ Sum += V[I];
+ V.foo();
+ }
+
+ for (int I = 0; I < V.size(); ++I) {
+ Sum += V[I];
+ dependent<int>::iterator It = V.begin();
+ }
+}
+
+} // namespace NegativePseudoArray
+
+namespace NegativeMultiEndCall {
+
+S Ss;
+T Tt;
+U Uu;
+
+void f(X);
+void f(S);
+void f(T);
+
+void complexContainer() {
+ X Xx;
+ for (S::iterator I = Xx.Ss.begin(), E = Xx.Ss.end(); I != E; ++I) {
+ f(Xx);
+ MutableVal K = *I;
+ }
+
+ for (T::iterator I = Xx.Tt.begin(), E = Xx.Tt.end(); I != E; ++I) {
+ f(Xx);
+ int K = *I;
+ }
+
+ for (S::iterator I = Xx.Ss.begin(), E = Xx.Ss.end(); I != E; ++I) {
+ f(Xx.Ss);
+ MutableVal K = *I;
+ }
+
+ for (T::iterator I = Xx.Tt.begin(), E = Xx.Tt.end(); I != E; ++I) {
+ f(Xx.Tt);
+ int K = *I;
+ }
+
+ for (S::iterator I = Xx.getS().begin(), E = Xx.getS().end(); I != E; ++I) {
+ f(Xx.getS());
+ MutableVal K = *I;
+ }
+
+ X Exes[5];
+ int Index = 0;
+
+ for (S::iterator I = Exes[Index].getS().begin(),
+ E = Exes[Index].getS().end();
+ I != E; ++I) {
+ Index++;
+ MutableVal K = *I;
+ }
+}
+
+} // namespace NegativeMultiEndCall
+
+namespace NoUsages {
+
+const int N = 6;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+S Ss;
+dependent<int> V;
+int Count = 0;
+
+void foo();
+
+void f() {
+ for (int I = 0; I < N; ++I) {}
+ for (int I = 0; I < N; ++I)
+ printf("Hello world\n");
+ for (int I = 0; I < N; ++I)
+ ++Count;
+ for (int I = 0; I < N; ++I)
+ foo();
+
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I) {}
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ printf("Hello world\n");
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ ++Count;
+ for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+ foo();
+
+ for (int I = 0; I < V.size(); ++I) {}
+ for (int I = 0; I < V.size(); ++I)
+ printf("Hello world\n");
+ for (int I = 0; I < V.size(); ++I)
+ ++Count;
+ for (int I = 0; I < V.size(); ++I)
+ foo();
+}
+
+} // namespace NoUsages
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'UPPER_CASE'}]}" \
+// RUN: -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int N = 10;
+int ARR[N];
+int NUMS[N];
+int NUMS_[N];
+
+void naming() {
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", ARR[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+ // CHECK-FIXES: for (int I : ARR)
+ // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", NUMS[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int NUM : NUMS)
+ // CHECK-FIXES-NEXT: printf("%d\n", NUM);
+
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", NUMS_[I]);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int NUM : NUMS_)
+ // CHECK-FIXES-NEXT: printf("%d\n", NUM);
+
+ int NUM = 0;
+ for (int I = 0; I < N; ++I) {
+ printf("%d\n", NUMS[I] + NUM);
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+ // CHECK-FIXES: for (int I : NUMS)
+ // CHECK-FIXES-NEXT: printf("%d\n", I + NUM);
+}
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,modernize-loop-convert -- -std=c11 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+int arr[6] = {1, 2, 3, 4, 5, 6};
+
+void f(void) {
+ for (int i = 0; i < 6; ++i) {
+ (void)arr[i];
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-make-shared %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: modernize-make-shared.MakeSmartPtrFunction, \
+// RUN: value: 'my::MakeShared'}, \
+// RUN: {key: modernize-make-shared.MakeSmartPtrFunctionHeader, \
+// RUN: value: 'make_shared_util.h'} \
+// RUN: ]}" \
+// RUN: -- -std=c++11 -I%S/Inputs/modernize-smart-ptr
+
+#include "shared_ptr.h"
+// CHECK-FIXES: #include "make_shared_util.h"
+
+void f() {
+ std::shared_ptr<int> P1 = std::shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use my::MakeShared instead
+ // CHECK-FIXES: std::shared_ptr<int> P1 = my::MakeShared<int>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-make-shared %t -- -- -std=c++11 \
+// RUN: -I%S/Inputs/modernize-smart-ptr
+
+#include "shared_ptr.h"
+// CHECK-FIXES: #include <memory>
+
+struct Base {
+ Base();
+ Base(int, int);
+};
+
+struct Derived : public Base {
+ Derived();
+ Derived(int, int);
+};
+
+struct APair {
+ int a, b;
+};
+
+struct DPair {
+ DPair() : a(0), b(0) {}
+ DPair(int x, int y) : a(y), b(x) {}
+ int a, b;
+};
+
+struct Empty {};
+
+template <class T>
+using shared_ptr_ = std::shared_ptr<T>;
+
+void *operator new(__SIZE_TYPE__ Count, void *Ptr);
+
+int g(std::shared_ptr<int> P);
+
+std::shared_ptr<Base> getPointer() {
+ return std::shared_ptr<Base>(new Base);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_shared instead
+ // CHECK-FIXES: return std::make_shared<Base>();
+}
+
+void basic() {
+ std::shared_ptr<int> P1 = std::shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: std::shared_ptr<int> P1 = std::make_shared<int>();
+
+ P1.reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: P1 = std::make_shared<int>();
+
+ P1 = std::shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: P1 = std::make_shared<int>();
+
+ // Without parenthesis.
+ std::shared_ptr<int> P2 = std::shared_ptr<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: std::shared_ptr<int> P2 = std::make_shared<int>();
+
+ P2.reset(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: P2 = std::make_shared<int>();
+
+ P2 = std::shared_ptr<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead [modernize-make-shared]
+ // CHECK-FIXES: P2 = std::make_shared<int>();
+
+ // With auto.
+ auto P3 = std::shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead
+ // CHECK-FIXES: auto P3 = std::make_shared<int>();
+
+ {
+ // No std.
+ using namespace std;
+ shared_ptr<int> Q = shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_shared instead
+ // CHECK-FIXES: shared_ptr<int> Q = std::make_shared<int>();
+
+ Q = shared_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+ // CHECK-FIXES: Q = std::make_shared<int>();
+ }
+
+ std::shared_ptr<int> R(new int());
+
+ // Create the shared_ptr as a parameter to a function.
+ int T = g(std::shared_ptr<int>(new int()));
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead
+ // CHECK-FIXES: int T = g(std::make_shared<int>());
+
+ // Only replace if the type in the template is the same as the type returned
+ // by the new operator.
+ auto Pderived = std::shared_ptr<Base>(new Derived());
+
+ // OK to replace for reset and assign
+ Pderived.reset(new Derived());
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_shared instead
+ // CHECK-FIXES: Pderived = std::make_shared<Derived>();
+
+ Pderived = std::shared_ptr<Derived>(new Derived());
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use std::make_shared instead
+ // CHECK-FIXES: Pderived = std::make_shared<Derived>();
+
+ // FIXME: OK to replace if assigned to shared_ptr<Base>
+ Pderived = std::shared_ptr<Base>(new Derived());
+
+ // FIXME: OK to replace when auto is not used
+ std::shared_ptr<Base> PBase = std::shared_ptr<Base>(new Derived());
+
+ // The pointer is returned by the function, nothing to do.
+ std::shared_ptr<Base> RetPtr = getPointer();
+
+ // This emulates std::move.
+ std::shared_ptr<int> Move = static_cast<std::shared_ptr<int> &&>(P1);
+
+ // Placement arguments should not be removed.
+ int *PInt = new int;
+ std::shared_ptr<int> Placement = std::shared_ptr<int>(new (PInt) int{3});
+ Placement.reset(new (PInt) int{3});
+ Placement = std::shared_ptr<int>(new (PInt) int{3});
+}
+
+// Calling make_smart_ptr from within a member function of a type with a
+// private or protected constructor would be ill-formed.
+class Private {
+private:
+ Private(int z) {}
+
+public:
+ Private() {}
+ void create() {
+ auto callsPublic = std::shared_ptr<Private>(new Private);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+ // CHECK-FIXES: auto callsPublic = std::make_shared<Private>();
+ auto ptr = std::shared_ptr<Private>(new Private(42));
+ ptr.reset(new Private(42));
+ ptr = std::shared_ptr<Private>(new Private(42));
+ }
+
+ virtual ~Private();
+};
+
+class Protected {
+protected:
+ Protected() {}
+
+public:
+ Protected(int, int) {}
+ void create() {
+ auto callsPublic = std::shared_ptr<Protected>(new Protected(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+ // CHECK-FIXES: auto callsPublic = std::make_shared<Protected>(1, 2);
+ auto ptr = std::shared_ptr<Protected>(new Protected);
+ ptr.reset(new Protected);
+ ptr = std::shared_ptr<Protected>(new Protected);
+ }
+};
+
+void initialization(int T, Base b) {
+ // Test different kinds of initialization of the pointee.
+
+ // Direct initialization with parenthesis.
+ std::shared_ptr<DPair> PDir1 = std::shared_ptr<DPair>(new DPair(1, T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir1 = std::make_shared<DPair>(1, T);
+ PDir1.reset(new DPair(1, T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+ // CHECK-FIXES: PDir1 = std::make_shared<DPair>(1, T);
+
+ // Direct initialization with braces.
+ std::shared_ptr<DPair> PDir2 = std::shared_ptr<DPair>(new DPair{2, T});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir2 = std::make_shared<DPair>(2, T);
+ PDir2.reset(new DPair{2, T});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+ // CHECK-FIXES: PDir2 = std::make_shared<DPair>(2, T);
+
+ // Aggregate initialization.
+ std::shared_ptr<APair> PAggr = std::shared_ptr<APair>(new APair{T, 1});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<APair> PAggr = std::make_shared<APair>(APair{T, 1});
+ PAggr.reset(new APair{T, 1});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+ // CHECK-FIXES: std::make_shared<APair>(APair{T, 1});
+
+ // Test different kinds of initialization of the pointee, when the shared_ptr
+ // is initialized with braces.
+
+ // Direct initialization with parenthesis.
+ std::shared_ptr<DPair> PDir3 = std::shared_ptr<DPair>{new DPair(3, T)};
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir3 = std::make_shared<DPair>(3, T);
+
+ // Direct initialization with braces.
+ std::shared_ptr<DPair> PDir4 = std::shared_ptr<DPair>{new DPair{4, T}};
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir4 = std::make_shared<DPair>(4, T);
+
+ // Aggregate initialization.
+ std::shared_ptr<APair> PAggr2 = std::shared_ptr<APair>{new APair{T, 2}};
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<APair> PAggr2 = std::make_shared<APair>(APair{T, 2});
+
+ // Direct initialization with parenthesis, without arguments.
+ std::shared_ptr<DPair> PDir5 = std::shared_ptr<DPair>(new DPair());
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir5 = std::make_shared<DPair>();
+
+ // Direct initialization with braces, without arguments.
+ std::shared_ptr<DPair> PDir6 = std::shared_ptr<DPair>(new DPair{});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<DPair> PDir6 = std::make_shared<DPair>();
+
+ // Aggregate initialization without arguments.
+ std::shared_ptr<Empty> PEmpty = std::shared_ptr<Empty>(new Empty{});
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<Empty> PEmpty = std::make_shared<Empty>(Empty{});
+}
+
+void aliases() {
+ typedef std::shared_ptr<int> IntPtr;
+ IntPtr Typedef = IntPtr(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use std::make_shared instead
+ // CHECK-FIXES: IntPtr Typedef = std::make_shared<int>();
+
+ // We use 'bool' instead of '_Bool'.
+ typedef std::shared_ptr<bool> BoolPtr;
+ BoolPtr BoolType = BoolPtr(new bool);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::make_shared instead
+ // CHECK-FIXES: BoolPtr BoolType = std::make_shared<bool>();
+
+ // We use 'Base' instead of 'struct Base'.
+ typedef std::shared_ptr<Base> BasePtr;
+ BasePtr StructType = BasePtr(new Base);
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+// CHECK-FIXES: BasePtr StructType = std::make_shared<Base>();
+
+#define PTR shared_ptr<int>
+ std::shared_ptr<int> Macro = std::PTR(new int);
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead
+// CHECK-FIXES: std::shared_ptr<int> Macro = std::make_shared<int>();
+#undef PTR
+
+ std::shared_ptr<int> Using = shared_ptr_<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead
+ // CHECK-FIXES: std::shared_ptr<int> Using = std::make_shared<int>();
+}
+
+void whitespaces() {
+ // clang-format off
+ auto Space = std::shared_ptr <int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use std::make_shared instead
+ // CHECK-FIXES: auto Space = std::make_shared<int>();
+
+ auto Spaces = std :: shared_ptr <int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use std::make_shared instead
+ // CHECK-FIXES: auto Spaces = std::make_shared<int>();
+ // clang-format on
+}
+
+void nesting() {
+ auto Nest = std::shared_ptr<std::shared_ptr<int>>(new std::shared_ptr<int>(new int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use std::make_shared instead
+ // CHECK-FIXES: auto Nest = std::make_shared<std::shared_ptr<int>>(new int);
+ Nest.reset(new std::shared_ptr<int>(new int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead
+ // CHECK-FIXES: Nest = std::make_shared<std::shared_ptr<int>>(new int);
+ Nest->reset(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+ // CHECK-FIXES: *Nest = std::make_shared<int>();
+}
+
+void reset() {
+ std::shared_ptr<int> P;
+ P.reset();
+ P.reset(nullptr);
+ P.reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use std::make_shared instead
+ // CHECK-FIXES: P = std::make_shared<int>();
+
+ auto Q = &P;
+ Q->reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead
+ // CHECK-FIXES: *Q = std::make_shared<int>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-make-unique %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: modernize-make-unique.MakeSmartPtrFunction, \
+// RUN: value: 'my::MakeUnique'}, \
+// RUN: {key: modernize-make-unique.MakeSmartPtrFunctionHeader, \
+// RUN: value: 'make_unique_util.h'} \
+// RUN: ]}" \
+// RUN: -- -std=c++11 -I%S/Inputs/modernize-smart-ptr
+
+#include "unique_ptr.h"
+// CHECK-FIXES: #include "make_unique_util.h"
+
+void f() {
+ std::unique_ptr<int> P1 = std::unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use my::MakeUnique instead
+ // CHECK-FIXES: std::unique_ptr<int> P1 = my::MakeUnique<int>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-make-unique %t -- -- -std=c++11 \
+// RUN: -I%S/Inputs/modernize-smart-ptr
+
+#include "unique_ptr.h"
+// CHECK-FIXES: #include <memory>
+
+struct Base {
+ Base();
+ Base(int, int);
+};
+
+struct Derived : public Base {
+ Derived();
+ Derived(int, int);
+};
+
+struct APair {
+ int a, b;
+};
+
+struct DPair {
+ DPair() : a(0), b(0) {}
+ DPair(int x, int y) : a(y), b(x) {}
+ int a, b;
+};
+
+struct Empty {};
+
+namespace {
+class Foo {};
+} // namespace
+
+namespace bar {
+class Bar {};
+} // namespace bar
+
+template <class T>
+using unique_ptr_ = std::unique_ptr<T>;
+
+void *operator new(__SIZE_TYPE__ Count, void *Ptr);
+
+int g(std::unique_ptr<int> P);
+
+std::unique_ptr<Base> getPointer() {
+ return std::unique_ptr<Base>(new Base);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_unique instead
+ // CHECK-FIXES: return std::make_unique<Base>();
+}
+
+void basic() {
+ std::unique_ptr<int> P1 = std::unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: std::unique_ptr<int> P1 = std::make_unique<int>();
+
+ P1.reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: P1 = std::make_unique<int>();
+
+ P1 = std::unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: P1 = std::make_unique<int>();
+
+ // Without parenthesis.
+ std::unique_ptr<int> P2 = std::unique_ptr<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: std::unique_ptr<int> P2 = std::make_unique<int>();
+
+ P2.reset(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: P2 = std::make_unique<int>();
+
+ P2 = std::unique_ptr<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead [modernize-make-unique]
+ // CHECK-FIXES: P2 = std::make_unique<int>();
+
+ // With auto.
+ auto P3 = std::unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead
+ // CHECK-FIXES: auto P3 = std::make_unique<int>();
+
+ {
+ // No std.
+ using namespace std;
+ unique_ptr<int> Q = unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_unique instead
+ // CHECK-FIXES: unique_ptr<int> Q = std::make_unique<int>();
+
+ Q = unique_ptr<int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+ // CHECK-FIXES: Q = std::make_unique<int>();
+ }
+
+ std::unique_ptr<int> R(new int());
+
+ // Create the unique_ptr as a parameter to a function.
+ int T = g(std::unique_ptr<int>(new int()));
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead
+ // CHECK-FIXES: int T = g(std::make_unique<int>());
+
+ // Only replace if the type in the template is the same as the type returned
+ // by the new operator.
+ auto Pderived = std::unique_ptr<Base>(new Derived());
+
+ // OK to replace for reset and assign
+ Pderived.reset(new Derived());
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_unique instead
+ // CHECK-FIXES: Pderived = std::make_unique<Derived>();
+
+ Pderived = std::unique_ptr<Derived>(new Derived());
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use std::make_unique instead
+ // CHECK-FIXES: Pderived = std::make_unique<Derived>();
+
+ // FIXME: OK to replace if assigned to unique_ptr<Base>
+ Pderived = std::unique_ptr<Base>(new Derived());
+
+ // FIXME: OK to replace when auto is not used
+ std::unique_ptr<Base> PBase = std::unique_ptr<Base>(new Derived());
+
+ // The pointer is returned by the function, nothing to do.
+ std::unique_ptr<Base> RetPtr = getPointer();
+
+ // This emulates std::move.
+ std::unique_ptr<int> Move = static_cast<std::unique_ptr<int> &&>(P1);
+
+ // Placement arguments should not be removed.
+ int *PInt = new int;
+ std::unique_ptr<int> Placement = std::unique_ptr<int>(new (PInt) int{3});
+ Placement.reset(new (PInt) int{3});
+ Placement = std::unique_ptr<int>(new (PInt) int{3});
+}
+
+// Calling make_smart_ptr from within a member function of a type with a
+// private or protected constructor would be ill-formed.
+class Private {
+private:
+ Private(int z) {}
+
+public:
+ Private() {}
+ void create() {
+ auto callsPublic = std::unique_ptr<Private>(new Private);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+ // CHECK-FIXES: auto callsPublic = std::make_unique<Private>();
+ auto ptr = std::unique_ptr<Private>(new Private(42));
+ ptr.reset(new Private(42));
+ ptr = std::unique_ptr<Private>(new Private(42));
+ }
+
+ virtual ~Private();
+};
+
+class Protected {
+protected:
+ Protected() {}
+
+public:
+ Protected(int, int) {}
+ void create() {
+ auto callsPublic = std::unique_ptr<Protected>(new Protected(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+ // CHECK-FIXES: auto callsPublic = std::make_unique<Protected>(1, 2);
+ auto ptr = std::unique_ptr<Protected>(new Protected);
+ ptr.reset(new Protected);
+ ptr = std::unique_ptr<Protected>(new Protected);
+ }
+};
+
+void initialization(int T, Base b) {
+ // Test different kinds of initialization of the pointee.
+
+ // Direct initialization with parenthesis.
+ std::unique_ptr<DPair> PDir1 = std::unique_ptr<DPair>(new DPair(1, T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir1 = std::make_unique<DPair>(1, T);
+ PDir1.reset(new DPair(1, T));
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+ // CHECK-FIXES: PDir1 = std::make_unique<DPair>(1, T);
+
+ // Direct initialization with braces.
+ std::unique_ptr<DPair> PDir2 = std::unique_ptr<DPair>(new DPair{2, T});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir2 = std::make_unique<DPair>(2, T);
+ PDir2.reset(new DPair{2, T});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+ // CHECK-FIXES: PDir2 = std::make_unique<DPair>(2, T);
+
+ // Aggregate initialization.
+ std::unique_ptr<APair> PAggr = std::unique_ptr<APair>(new APair{T, 1});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<APair> PAggr = std::make_unique<APair>(APair{T, 1});
+ PAggr.reset(new APair{T, 1});
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+ // CHECK-FIXES: std::make_unique<APair>(APair{T, 1});
+
+ // Test different kinds of initialization of the pointee, when the unique_ptr
+ // is initialized with braces.
+
+ // Direct initialization with parenthesis.
+ std::unique_ptr<DPair> PDir3 = std::unique_ptr<DPair>{new DPair(3, T)};
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir3 = std::make_unique<DPair>(3, T);
+
+ // Direct initialization with braces.
+ std::unique_ptr<DPair> PDir4 = std::unique_ptr<DPair>{new DPair{4, T}};
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir4 = std::make_unique<DPair>(4, T);
+
+ // Aggregate initialization.
+ std::unique_ptr<APair> PAggr2 = std::unique_ptr<APair>{new APair{T, 2}};
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<APair> PAggr2 = std::make_unique<APair>(APair{T, 2});
+
+ // Direct initialization with parenthesis, without arguments.
+ std::unique_ptr<DPair> PDir5 = std::unique_ptr<DPair>(new DPair());
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir5 = std::make_unique<DPair>();
+
+ // Direct initialization with braces, without arguments.
+ std::unique_ptr<DPair> PDir6 = std::unique_ptr<DPair>(new DPair{});
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<DPair> PDir6 = std::make_unique<DPair>();
+
+ // Aggregate initialization without arguments.
+ std::unique_ptr<Empty> PEmpty = std::unique_ptr<Empty>(new Empty{});
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<Empty> PEmpty = std::make_unique<Empty>(Empty{});
+
+ std::unique_ptr<Foo> FF = std::unique_ptr<Foo>(new Foo());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning:
+ // CHECK-FIXES: std::unique_ptr<Foo> FF = std::make_unique<Foo>();
+ FF.reset(new Foo());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: FF = std::make_unique<Foo>();
+
+ std::unique_ptr<bar::Bar> BB = std::unique_ptr<bar::Bar>(new bar::Bar());
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning:
+ // CHECK-FIXES: std::unique_ptr<bar::Bar> BB = std::make_unique<bar::Bar>();
+ BB.reset(new bar::Bar());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: BB = std::make_unique<bar::Bar>();
+
+ std::unique_ptr<Foo[]> FFs;
+ FFs.reset(new Foo[5]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+ // CHECK-FIXES: FFs = std::make_unique<Foo[]>(5);
+ FFs.reset(new Foo[5]());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+ // CHECK-FIXES: FFs = std::make_unique<Foo[]>(5);
+ const int Num = 1;
+ FFs.reset(new Foo[Num]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+ // CHECK-FIXES: FFs = std::make_unique<Foo[]>(Num);
+ int Num2 = 1;
+ FFs.reset(new Foo[Num2]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+ // CHECK-FIXES: FFs = std::make_unique<Foo[]>(Num2);
+
+ std::unique_ptr<int[]> FI;
+ FI.reset(new int[5]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: FI = std::make_unique<int[]>(5);
+ FI.reset(new int[5]());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: FI = std::make_unique<int[]>(5);
+ FI.reset(new int[Num]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: FI = std::make_unique<int[]>(Num);
+ FI.reset(new int[Num2]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+ // CHECK-FIXES: FI = std::make_unique<int[]>(Num2);
+}
+
+void aliases() {
+ typedef std::unique_ptr<int> IntPtr;
+ IntPtr Typedef = IntPtr(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use std::make_unique instead
+ // CHECK-FIXES: IntPtr Typedef = std::make_unique<int>();
+
+ // We use 'bool' instead of '_Bool'.
+ typedef std::unique_ptr<bool> BoolPtr;
+ BoolPtr BoolType = BoolPtr(new bool);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::make_unique instead
+ // CHECK-FIXES: BoolPtr BoolType = std::make_unique<bool>();
+
+ // We use 'Base' instead of 'struct Base'.
+ typedef std::unique_ptr<Base> BasePtr;
+ BasePtr StructType = BasePtr(new Base);
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+// CHECK-FIXES: BasePtr StructType = std::make_unique<Base>();
+
+#define PTR unique_ptr<int>
+ std::unique_ptr<int> Macro = std::PTR(new int);
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_unique instead
+// CHECK-FIXES: std::unique_ptr<int> Macro = std::make_unique<int>();
+#undef PTR
+
+ std::unique_ptr<int> Using = unique_ptr_<int>(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_unique instead
+ // CHECK-FIXES: std::unique_ptr<int> Using = std::make_unique<int>();
+}
+
+void whitespaces() {
+ // clang-format off
+ auto Space = std::unique_ptr <int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use std::make_unique instead
+ // CHECK-FIXES: auto Space = std::make_unique<int>();
+
+ auto Spaces = std :: unique_ptr <int>(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use std::make_unique instead
+ // CHECK-FIXES: auto Spaces = std::make_unique<int>();
+ // clang-format on
+}
+
+void nesting() {
+ auto Nest = std::unique_ptr<std::unique_ptr<int>>(new std::unique_ptr<int>(new int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use std::make_unique instead
+ // CHECK-FIXES: auto Nest = std::make_unique<std::unique_ptr<int>>(new int);
+ Nest.reset(new std::unique_ptr<int>(new int));
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead
+ // CHECK-FIXES: Nest = std::make_unique<std::unique_ptr<int>>(new int);
+ Nest->reset(new int);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+ // CHECK-FIXES: *Nest = std::make_unique<int>();
+}
+
+void reset() {
+ std::unique_ptr<int> P;
+ P.reset();
+ P.reset(nullptr);
+ P.reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use std::make_unique instead
+ // CHECK-FIXES: P = std::make_unique<int>();
+
+ auto Q = &P;
+ Q->reset(new int());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead
+ // CHECK-FIXES: *Q = std::make_unique<int>();
+}
--- /dev/null
+// RUN: cp %S/Inputs/modernize-pass-by-value/header.h %T/pass-by-value-header.h
+// RUN: clang-tidy %s -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %T | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:"
+// RUN: FileCheck -input-file=%T/pass-by-value-header.h %s -check-prefix=CHECK-FIXES
+
+#include "pass-by-value-header.h"
+// CHECK-MESSAGES: :8:5: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: #include <utility>
+// CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-pass-by-value %t -- -- -std=c++11 -isystem %S/Inputs/Headers
+
+// CHECK-FIXES: #include <utility>
+
+#define HEADER <./a.h>
+#include HEADER
+
+struct A {
+ A(const A &) {}
+ A(A &&) {}
+};
+
+struct B {
+ B(const A &a) : a(a) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: B(A a) : a(std::move(a)) {}
+ A a;
+};
--- /dev/null
+// RUN: cat %S/Inputs/modernize-pass-by-value/header-with-fix.h > %T/pass-by-value-header-with-fix.h
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %T | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:"
+// RUN: FileCheck -input-file=%t.cpp %s -check-prefix=CHECK-FIXES
+// RUN: FileCheck -input-file=%T/pass-by-value-header-with-fix.h %s -check-prefix=CHECK-HEADER-FIXES
+
+#include "pass-by-value-header-with-fix.h"
+// CHECK-HEADER-FIXES: Foo(S s);
+Foo::Foo(const S &s) : s(s) {}
+// CHECK-MESSAGES: :9:10: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: #include <utility>
+// CHECK-FIXES: Foo::Foo(S s) : s(std::move(s)) {}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-pass-by-value %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+namespace {
+// POD types are trivially move constructible.
+struct POD {
+ int a, b, c;
+};
+
+struct Movable {
+ int a, b, c;
+ Movable() = default;
+ Movable(const Movable &) {}
+ Movable(Movable &&) {}
+};
+
+struct NotMovable {
+ NotMovable() = default;
+ NotMovable(const NotMovable &) = default;
+ NotMovable(NotMovable &&) = delete;
+ int a, b, c;
+};
+}
+
+struct A {
+ A(const Movable &M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value]
+ // CHECK-FIXES: A(Movable M) : M(std::move(M)) {}
+ Movable M;
+};
+
+// Test that we aren't modifying other things than a parameter.
+Movable GlobalObj;
+struct B {
+ B(const Movable &M) : M(GlobalObj) {}
+ // CHECK-FIXES: B(const Movable &M) : M(GlobalObj) {}
+ Movable M;
+};
+
+// Test that a parameter with more than one reference to it won't be changed.
+struct C {
+ // Tests extra-reference in body.
+ C(const Movable &M) : M(M) { this->i = M.a; }
+ // CHECK-FIXES: C(const Movable &M) : M(M) { this->i = M.a; }
+
+ // Tests extra-reference in init-list.
+ C(const Movable &M, int) : M(M), i(M.a) {}
+ // CHECK-FIXES: C(const Movable &M, int) : M(M), i(M.a) {}
+ Movable M;
+ int i;
+};
+
+// Test that both declaration and definition are updated.
+struct D {
+ D(const Movable &M);
+ // CHECK-FIXES: D(Movable M);
+ Movable M;
+};
+D::D(const Movable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: D::D(Movable M) : M(std::move(M)) {}
+
+// Test with default parameter.
+struct E {
+ E(const Movable &M = Movable()) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: E(Movable M = Movable()) : M(std::move(M)) {}
+ Movable M;
+};
+
+// Test with object that can't be moved.
+struct F {
+ F(const NotMovable &NM) : NM(NM) {}
+ // CHECK-FIXES: F(const NotMovable &NM) : NM(NM) {}
+ NotMovable NM;
+};
+
+// Test unnamed parameter in declaration.
+struct G {
+ G(const Movable &);
+ // CHECK-FIXES: G(Movable );
+ Movable M;
+};
+G::G(const Movable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: G::G(Movable M) : M(std::move(M)) {}
+
+// Test parameter with and without qualifier.
+namespace ns_H {
+typedef ::Movable HMovable;
+}
+struct H {
+ H(const ns_H::HMovable &M);
+ // CHECK-FIXES: H(ns_H::HMovable M);
+ ns_H::HMovable M;
+};
+using namespace ns_H;
+H::H(const HMovable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: H(HMovable M) : M(std::move(M)) {}
+
+// Try messing up with macros.
+#define MOVABLE_PARAM(Name) const Movable & Name
+// CHECK-FIXES: #define MOVABLE_PARAM(Name) const Movable & Name
+struct I {
+ I(MOVABLE_PARAM(M)) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: I(MOVABLE_PARAM(M)) : M(M) {}
+ Movable M;
+};
+#undef MOVABLE_PARAM
+
+// Test that templates aren't modified.
+template <typename T> struct J {
+ J(const T &M) : M(M) {}
+ // CHECK-FIXES: J(const T &M) : M(M) {}
+ T M;
+};
+J<Movable> j1(Movable());
+J<NotMovable> j2(NotMovable());
+
+struct K_Movable {
+ K_Movable() = default;
+ K_Movable(const K_Movable &) = default;
+ K_Movable(K_Movable &&o) { dummy = o.dummy; }
+ int dummy;
+};
+
+// Test with movable type with an user defined move constructor.
+struct K {
+ K(const K_Movable &M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: K(K_Movable M) : M(std::move(M)) {}
+ K_Movable M;
+};
+
+template <typename T> struct L {
+ L(const Movable &M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: L(Movable M) : M(std::move(M)) {}
+ Movable M;
+};
+L<int> l(Movable());
+
+// Test with a non-instantiated template class.
+template <typename T> struct N {
+ N(const Movable &M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: N(Movable M) : M(std::move(M)) {}
+
+ Movable M;
+ T A;
+};
+
+// Test with value parameter.
+struct O {
+ O(Movable M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: O(Movable M) : M(std::move(M)) {}
+ Movable M;
+};
+
+// Test with a const-value parameter.
+struct P {
+ P(const Movable M) : M(M) {}
+ // CHECK-FIXES: P(const Movable M) : M(M) {}
+ Movable M;
+};
+
+// Test with multiples parameters where some need to be changed and some don't.
+// need to.
+struct Q {
+ Q(const Movable &A, const Movable &B, const Movable &C, double D)
+ : A(A), B(B), C(C), D(D) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: pass by value and use std::move
+ // CHECK-MESSAGES: :[[@LINE-3]]:41: warning: pass by value and use std::move
+ // CHECK-FIXES: Q(const Movable &A, Movable B, Movable C, double D)
+ // CHECK-FIXES: : A(A), B(std::move(B)), C(std::move(C)), D(D) {}
+ const Movable &A;
+ Movable B;
+ Movable C;
+ double D;
+};
+
+// Test that value-parameters with a nested name specifier are left as-is.
+namespace ns_R {
+typedef ::Movable RMovable;
+}
+struct R {
+ R(ns_R::RMovable M) : M(M) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+ // CHECK-FIXES: R(ns_R::RMovable M) : M(std::move(M)) {}
+ ns_R::RMovable M;
+};
+
+// Test with rvalue parameter.
+struct S {
+ S(Movable &&M) : M(M) {}
+ // CHECK-FIXES: S(Movable &&M) : M(M) {}
+ Movable M;
+};
+
+template <typename T, int N> struct array { T A[N]; };
+
+// Test that types that are trivially copyable will not use std::move. This will
+// cause problems with misc-move-const-arg, as it will revert it.
+struct T {
+ T(array<int, 10> a) : a_(a) {}
+ // CHECK-FIXES: T(array<int, 10> a) : a_(a) {}
+ array<int, 10> a_;
+};
+
+struct U {
+ U(const POD &M) : M(M) {}
+ POD M;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t -- -config='{CheckOptions: [{key: "modernize-raw-string-literal.DelimiterStem", value: "str"}, {key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}' -- -std=c++11
+
+char const *const ContainsSentinel{"who\\ops)\""};
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsSentinel{R"str(who\ops)")str"};{{$}}
+
+//char const *const ContainsDelim{"whoops)\")lit\""};
+// CHECK-XMESSAGES: :[[@LINE-1]]:33: warning: {{.*}} can be written as a raw string literal
+// CHECK-XFIXES: {{^}}char const *const ContainsDelim{R"lit1(whoops)")lit")lit1"};{{$}}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t
+
+// Don't replace these, because the raw literal would be longer.
+char const *const JustAQuote("quote:\'");
+char const *const NeedDelimiter("\":)\"");
+
+char const *const ManyQuotes("quotes:\'\'\'\'");
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ManyQuotes(R"(quotes:'''')");{{$}}
+
+char const *const LongOctal("\042\072\051\042");
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const LongOctal(R"lit(":)")lit");{{$}}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t -- -config="{CheckOptions: [{key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}" -- -std=c++11
+
+char const *const BackSlash("goink\\frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: escaped string literal can be written as a raw string literal [modernize-raw-string-literal]
+// CHECK-FIXES: {{^}}char const *const BackSlash(R"(goink\frob)");{{$}}
+
+char const *const PlainLiteral("plain literal");
+
+// Non-printable ASCII characters.
+char const *const Nul("goink\\\000");
+char const *const Soh("goink\\\001");
+char const *const Stx("goink\\\002");
+char const *const Etx("goink\\\003");
+char const *const Enq("goink\\\004");
+char const *const Ack("goink\\\005");
+char const *const Bell("goink\\\afrob");
+char const *const BackSpace("goink\\\bfrob");
+char const *const HorizontalTab("goink\\\tfrob");
+char const *const NewLine("goink\nfrob");
+char const *const VerticalTab("goink\\\vfrob");
+char const *const FormFeed("goink\\\ffrob");
+char const *const CarraigeReturn("goink\\\rfrob");
+char const *const So("goink\\\016");
+char const *const Si("goink\\\017");
+char const *const Dle("goink\\\020");
+char const *const Dc1("goink\\\021");
+char const *const Dc2("goink\\\022");
+char const *const Dc3("goink\\\023");
+char const *const Dc4("goink\\\024");
+char const *const Nak("goink\\\025");
+char const *const Syn("goink\\\026");
+char const *const Etb("goink\\\027");
+char const *const Can("goink\\\030");
+char const *const Em("goink\\\031");
+char const *const Sub("goink\\\032");
+char const *const Esc("goink\\\033");
+char const *const Fs("goink\\\034");
+char const *const Gs("goink\\\035");
+char const *const Rs("goink\\\036");
+char const *const Us("goink\\\037");
+char const *const HexNonPrintable("\\\x03");
+char const *const Delete("\\\177");
+
+char const *const TrailingSpace("A line \\with space. \n");
+char const *const TrailingNewLine("A single \\line.\n");
+char const *const AlreadyRaw(R"(foobie\\bletch)");
+char const *const UTF8Literal(u8"foobie\\bletch");
+char const *const UTF8RawLiteral(u8R"(foobie\\bletch)");
+// TODO: enable these tests once all supported compilers
+// support char16_t and char32_t (VS2013 does not)
+// char16_t const *const UTF16Literal(u"foobie\\bletch");
+// char16_t const *const UTF16RawLiteral(uR"(foobie\\bletch)");
+// char32_t const *const UTF32Literal(U"foobie\\bletch");
+// char32_t const *const UTF32RawLiteral(UR"(foobie\\bletch)");
+wchar_t const *const WideLiteral(L"foobie\\bletch");
+wchar_t const *const WideRawLiteral(LR"(foobie\\bletch)");
+
+char const *const SingleQuote("goink\'frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: {{.*}} can be written as a raw string literal
+// CHECK-XFIXES: {{^}}char const *const SingleQuote(R"(goink'frob)");{{$}}
+
+char const *const DoubleQuote("goink\"frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const DoubleQuote(R"(goink"frob)");{{$}}
+
+char const *const QuestionMark("goink\?frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const QuestionMark(R"(goink?frob)");{{$}}
+
+char const *const RegEx("goink\\(one|two\\)\\\\\\?.*\\nfrob");
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const RegEx(R"(goink\(one|two\)\\\?.*\nfrob)");{{$}}
+
+char const *const Path("C:\\Program Files\\Vendor\\Application\\Application.exe");
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const Path(R"(C:\Program Files\Vendor\Application\Application.exe)");{{$}}
+
+char const *const ContainsSentinel("who\\ops)\"");
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsSentinel(R"lit(who\ops)")lit");{{$}}
+
+char const *const ContainsDelim("whoops)\")lit\"");
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsDelim(R"lit1(whoops)")lit")lit1");{{$}}
+
+char const *const OctalPrintable("\100\\");
+// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const OctalPrintable(R"(@\)");{{$}}
+
+char const *const HexPrintable("\x40\\");
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const HexPrintable(R"(@\)");{{$}}
+
+char const *const prettyFunction(__PRETTY_FUNCTION__);
+char const *const function(__FUNCTION__);
+char const *const func(__func__);
+
+#define TRICK(arg_) #arg_
+char const *const MacroBody = TRICK(foo\\bar);
+
+#define HAT(rabbit_) #rabbit_ "foo\\bar"
+char const *const StringizedMacroArgument = HAT(foo\\bar);
+
+#define SUBST(lit_) lit_
+char const *const MacroArgument = SUBST("foo\\bar");
+// FIXME: We should be able to replace this string literal macro argument
+
+template <typename T>
+void fn(char const *const Arg) {
+ char const *const Str("foo\\bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+ // CHECK-FIXES: {{^}} char const *const Str(R"(foo\bar)");{{$}}
+}
+
+template <>
+void fn<int>(char const *const Arg) {
+ char const *const Str("foo\\bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+ // CHECK-FIXES: {{^}} char const *const Str(R"(foo\bar)");{{$}}
+}
+
+void callFn() {
+ fn<int>("foo\\bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} can be written as a raw string literal
+ // CHECK-FIXES: {{^}} fn<int>(R"(foo\bar)");{{$}}
+ fn<double>("foo\\bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} can be written as a raw string literal
+ // CHECK-FIXES: {{^}} fn<double>(R"(foo\bar)");{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-redundant-void-arg %t -- -- -fdelayed-template-parsing
+
+int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}int foo() {{{$}}
+ return 0;
+}
+
+template <class T>
+struct MyFoo {
+ int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}} int foo() {{{$}}
+ return 0;
+ }
+};
+// Explicit instantiation.
+template class MyFoo<int>;
+
+template <class T>
+struct MyBar {
+ // This declaration isn't instantiated and won't be parsed 'delayed-template-parsing'.
+ int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}} int foo() {{{$}}
+ return 0;
+ }
+};
--- /dev/null
+// RUN: clang-tidy -checks=-*,modernize-redundant-void-arg %s -- -x c | count 0
+
+#define NULL 0
+
+extern int i;
+
+int foo2() {
+ return 0;
+}
+
+int j = 1;
+
+int foo(void) {
+ return 0;
+}
+
+typedef unsigned int my_uint;
+
+typedef void my_void;
+
+// A function taking void and returning a pointer to function taking void
+// and returning int.
+int (*returns_fn_void_int(void))(void);
+
+typedef int (*returns_fn_void_int_t(void))(void);
+
+int (*returns_fn_void_int(void))(void) {
+ return NULL;
+}
+
+// A function taking void and returning a pointer to a function taking void
+// and returning a pointer to a function taking void and returning void.
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+
+typedef void (*(*returns_fn_returns_fn_void_void_t(void))(void))(void);
+
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void) {
+ return NULL;
+}
+
+void bar() {
+ int i;
+ int *pi = NULL;
+ void *pv = (void *) pi;
+ float f;
+ float *fi;
+ double d;
+ double *pd;
+}
+
+void (*f1)(void);
+void (*f2)(void) = NULL;
+void (*f3)(void) = bar;
+void (*fa)();
+void (*fb)() = NULL;
+void (*fc)() = bar;
+
+typedef void (function_ptr)(void);
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-redundant-void-arg %t
+
+#define NULL 0
+
+int foo();
+
+void bar();
+
+void bar2();
+
+extern "C" void ecfoo(void);
+
+extern "C" void ecfoo(void) {
+}
+
+extern int i;
+
+int j = 1;
+
+int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}int foo() {{{$}}
+ return 0;
+}
+
+typedef unsigned int my_uint;
+
+typedef void my_void;
+
+// A function taking void and returning a pointer to function taking void
+// and returning int.
+int (*returns_fn_void_int(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function declaration
+// CHECK-FIXES: {{^}}int (*returns_fn_void_int())();{{$}}
+
+typedef int (*returns_fn_void_int_t(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-2]]:44: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef int (*returns_fn_void_int_t())();{{$}}
+
+// Should work for type aliases as well as typedef.
+using returns_fn_void_int_t2 = int (*(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: {{.*}} in type alias
+// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: {{.*}} in type alias
+// CHECK-FIXES: {{^}}using returns_fn_void_int_t2 = int (*())();{{$}}
+
+int (*returns_fn_void_int(void))(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}int (*returns_fn_void_int())() {{{$}}
+ return nullptr;
+}
+
+// A function taking void and returning a pointer to a function taking void
+// and returning a pointer to a function taking void and returning void.
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function declaration
+// CHECK-FIXES: {{^}}void (*(*returns_fn_returns_fn_void_void())())();{{$}}
+
+typedef void (*(*returns_fn_returns_fn_void_void_t(void))(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:52: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-2]]:59: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-3]]:66: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (*(*returns_fn_returns_fn_void_void_t())())();{{$}}
+
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void (*(*returns_fn_returns_fn_void_void())())() {{{$}}
+ return nullptr;
+}
+
+void bar(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void bar() {{{$}}
+}
+
+void op_fn(int i) {
+}
+
+class gronk {
+public:
+ gronk();
+ ~gronk();
+
+ void foo();
+ void bar();
+ void bar2
+ ();
+ void operation(int i) { }
+
+private:
+ int m_i;
+ int *m_pi;
+ float m_f;
+ float *m_pf;
+ double m_d;
+ double *m_pd;
+
+ void (*f1)(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in field declaration
+ // CHECK-FIXES: {{^ }}void (*f1)();{{$}}
+
+ void (*op)(int i);
+
+ void (gronk::*p1)(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in field declaration
+ // CHECK-FIXES: {{^ }}void (gronk::*p1)();{{$}}
+
+ int (gronk::*p_mi);
+
+ void (gronk::*p2)(int);
+
+ void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in function declaration
+ // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: {{.*}} in function declaration
+ // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: {{.*}} in function declaration
+ // CHECK-FIXES: {{^}} void (*(*returns_fn_returns_fn_void_void())())();{{$}}
+
+ void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)(void))(void))(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:58: warning: {{.*}} in field declaration
+ // CHECK-MESSAGES: :[[@LINE-2]]:65: warning: {{.*}} in field declaration
+ // CHECK-MESSAGES: :[[@LINE-3]]:72: warning: {{.*}} in field declaration
+ // CHECK-FIXES: {{^}} void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)())())();{{$}}
+};
+
+int i;
+int *pi;
+void *pv = (void *) pi;
+float f;
+float *fi;
+double d;
+double *pd;
+
+void (*f1)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration
+// CHECK-FIXES: {{^}}void (*f1)();{{$}}
+
+void (*f2)(void) = nullptr;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2)() = nullptr;{{$}}
+
+void (*f2b)(void)(nullptr);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2b)()(nullptr);{{$}}
+
+void (*f2c)(void){nullptr};
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2c)(){nullptr};{{$}}
+
+void (*f2d)(void) = NULL;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2d)() = NULL;{{$}}
+
+void (*f2e)(void)(NULL);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2e)()(NULL);{{$}}
+
+void (*f2f)(void){NULL};
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2f)(){NULL};{{$}}
+
+void (*f3)(void) = bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f3)() = bar;{{$}}
+
+void (*o1)(int i);
+void (*o2)(int i) = nullptr;
+void (*o3)(int i)(nullptr);
+void (*o4)(int i){nullptr};
+void (*o5)(int i) = NULL;
+void (*o6)(int i)(NULL);
+void (*o7)(int i){NULL};
+void (*o8)(int i) = op_fn;
+
+void (*fa)();
+
+void (*fb)() = nullptr;
+
+void (*fc)() = bar;
+
+typedef void (function_ptr)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (function_ptr)();{{$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef void (function_ptr2)
+ (
+ void
+ );
+// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^typedef void \(function_ptr2\)$}}
+// CHECK-FIXES-NEXT: {{^ \($}}
+// CHECK-FIXES-NEXT: {{^ $}}
+// CHECK-FIXES-NEXT: {{^ \);$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef
+void
+(
+*
+(
+*
+returns_fn_returns_fn_void_void_t2
+(
+void
+)
+)
+(
+void
+)
+)
+(
+void
+)
+;
+// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^typedef$}}
+// CHECK-FIXES-NEXT: {{^void$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NEXT: {{^\*$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NEXT: {{^\*$}}
+// CHECK-FIXES-NEXT: {{^returns_fn_returns_fn_void_void_t2$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT: {{[^ ]}}
+// CHECK-FIXES: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT: {{[^ ]}}
+// CHECK-FIXES: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT: {{[^ ]}}
+// CHECK-FIXES: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^;$}}
+
+
+void (gronk::*p1)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration
+// CHECK-FIXES: {{^}}void (gronk::*p1)();{{$}}
+
+void (gronk::*p2)(void) = &gronk::foo;
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (gronk::*p2)() = &gronk::foo;{{$}}
+
+typedef void (gronk::*member_function_ptr)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (gronk::*member_function_ptr)();{{$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef void (gronk::*member_function_ptr2)
+ (
+ void
+ );
+// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^typedef void \(gronk::\*member_function_ptr2\)$}}
+// CHECK-FIXES-NEXT: {{^ \($}}
+// CHECK-FIXES-NEXT: {{^ $}}
+// CHECK-FIXES-NEXT: {{^ \);$}}
+
+void gronk::foo() {
+ void (*f1)(void) = &::bar;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-FIXES: {{^ }}void (*f1)() = &::bar;{{$}}
+
+ void (*f2)(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration
+ // CHECK-FIXES: {{^ }}void (*f2)();{{$}}
+
+ // intentionally not LLVM style to check preservation of whitesapce
+ void (*f3)
+ (
+ void
+ );
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration
+ // CHECK-FIXES: {{^ }}void (*f3){{$}}
+ // CHECK-FIXES-NEXT: {{^ \($}}
+ // CHECK-FIXES-NEXT: {{^ $}}
+ // CHECK-FIXES-NEXT: {{^ \);$}}
+}
+
+void gronk::bar(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void gronk::bar() {{{$}}
+ void (gronk::*p3)(void) = &gronk::foo;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration with initializer
+ // CHECK-FIXES: {{^ }}void (gronk::*p3)() = &gronk::foo;{{$}}
+
+ void (gronk::*p4)(void);
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration
+ // CHECK-FIXES: {{^ }}void (gronk::*p4)();{{$}}
+
+ // intentionally not LLVM style to check preservation of whitesapce
+ void (gronk::*p5)
+ (
+ void
+ );
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration
+ // CHECK-FIXES: {{^ }}void (gronk::*p5){{$}}
+ // CHECK-FIXES-NEXT: {{^ \($}}
+ // CHECK-FIXES-NExT: {{^ $}}
+ // CHECK-FIXES-NExT: {{^ \);$}}
+}
+
+// intentionally not LLVM style to check preservation of whitesapce
+void gronk::bar2
+ (
+ void
+ )
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^void gronk::bar2$}}
+// CHECK-FIXES-NEXT: {{^ \($}}
+// CHECK-FIXES-NEXT: {{^ $}}
+// CHECK-FIXES-NEXT: {{^ \)$}}
+{
+}
+
+gronk::gronk(void)
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}gronk::gronk(){{$}}
+ : f1(nullptr),
+ p1(nullptr) {
+}
+
+gronk::~gronk(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}gronk::~gronk() {{{$}}
+}
+
+class nutter {
+public:
+ nutter();
+};
+
+nutter::nutter(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}nutter::nutter() {{{$}}
+ void (*f3)(void) = static_cast<void (*)(void)>(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-2]]:43: warning: {{.*}} in named cast
+ // CHECK-FIXES: void (*f3)() = static_cast<void (*)()>(0);{{$}}
+
+ void (*f4)(void) = (void (*)(void)) 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: {{.*}} in cast expression
+ // CHECK-FIXES: void (*f4)() = (void (*)()) 0;{{$}}
+
+ void (*f5)(void) = reinterpret_cast<void (*)(void)>(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: {{.*}} in named cast
+ // CHECK-FIXES: void (*f5)() = reinterpret_cast<void (*)()>(0);{{$}}
+
+ // intentionally not LLVM style to check preservation of whitesapce
+ void (*f6)(void) = static_cast<void (*)
+ (
+ void
+ )>(0);
+ // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast
+ // CHECK-FIXES: {{^ }}void (*f6)() = static_cast<void (*){{$}}
+ // CHECK-FIXES-NEXT: {{^ \($}}
+ // CHECK-FIXES-NEXT: {{^ $}}
+ // CHECK-FIXES-NEXT: {{^ }})>(0);{{$}}
+
+ // intentionally not LLVM style to check preservation of whitesapce
+ void (*f7)(void) = (void (*)
+ (
+ void
+ )) 0;
+ // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in cast expression
+ // CHECK-FIXES: {{^ }}void (*f7)() = (void (*){{$}}
+ // CHECK-FIXES-NEXT: {{^ \($}}
+ // CHECK-FIXES-NEXT: {{^ $}}
+ // CHECK-FIXES-NEXT: {{^ \)\) 0;$}}
+
+ // intentionally not LLVM style to check preservation of whitesapce
+ void (*f8)(void) = reinterpret_cast<void (*)
+ (
+ void
+ )>(0);
+ // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast
+ // CHECK-FIXES: {{^ }}void (*f8)() = reinterpret_cast<void (*){{$}}
+ // CHECK-FIXES-NEXT: {{^ \($}}
+ // CHECK-FIXES-NEXT: {{^ $}}
+ // CHECK-FIXES-NEXT: {{^ \)>\(0\);$}}
+
+ void (*o1)(int) = static_cast<void (*)(int)>(0);
+ void (*o2)(int) = (void (*)(int)) 0;
+ void (*o3)(int) = reinterpret_cast<void (*)(int)>(0);
+}
+
+class generator {
+public:
+ int operator()(void) { return 1; }
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}} in function definition
+ // CHECK-FIXES: {{^ }}int operator()() { return 1; }{{$}}
+};
+
+void test_lambda_functions() {
+ auto lamb_duh = [](void (*fn)(void)) { (*fn)(); };
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}} in variable declaration
+ // CHECK-FIXES: {{^ }}auto lamb_duh = [](void (*fn)()) { (*fn)(); };{{$}}
+
+ auto lambda_generator = [](void) { return 1; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: {{.*}} in lambda expression
+ // CHECK-FIXES: {{^ }}auto lambda_generator = []() { return 1; };{{$}}
+
+ auto gen2 = []() { return 1; };
+
+ auto gen3 = []{ return 1; };
+
+ auto void_returner = [](void) -> void (*)(void) { return f1; };
+ // CHECK-MESSAGES: [[@LINE-1]]:27: warning: {{.*}} in lambda expression
+ // CHECK-MESSAGES: [[@LINE-2]]:45: warning: {{.*}} in lambda expression
+ // CHECK-FIXES: {{^ }}auto void_returner = []() -> void (*)() { return f1; };{{$}}
+}
+
+#define M(x) x
+
+M(void inmacro(void) {})
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition
+// CHECK-FIXES: M(void inmacro() {})
+
+#define F(A, B) \
+ struct F_##A##_##B { \
+ F_##A##_##B(void); \
+ }; \
+ F_##A##_##B::F_##A##_##B(void)
+
+F(Foo, Bar) {
+
+}
+
+struct DefinitionWithNoBody {
+ DefinitionWithNoBody(void) = delete;
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in function definition
+ // CHECK-FIXES: DefinitionWithNoBody() = delete;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-replace-auto-ptr %t -- -- \
+// RUN: -std=c++11 -I %S/Inputs/modernize-replace-auto-ptr
+
+// CHECK-FIXES: #include <utility>
+
+#include "memory.h"
+
+// Instrumentation for auto_ptr_ref test.
+struct Base {};
+struct Derived : Base {};
+std::auto_ptr<Derived> create_derived_ptr();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated, use unique_ptr instead [modernize-replace-auto-ptr]
+// CHECK-FIXES: std::unique_ptr<Derived> create_derived_ptr();
+
+
+// Test function return values (declaration)
+std::auto_ptr<char> f_5();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+// CHECK-FIXES: std::unique_ptr<char> f_5()
+
+
+// Test function parameters.
+void f_6(std::auto_ptr<int>);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+// CHECK-FIXES: void f_6(std::unique_ptr<int>);
+void f_7(const std::auto_ptr<int> &);
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: auto_ptr is deprecated
+// CHECK-FIXES: void f_7(const std::unique_ptr<int> &);
+
+
+// Test on record type fields.
+struct A {
+ std::auto_ptr<int> field;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> field;
+
+ typedef std::auto_ptr<int> int_ptr_type;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: auto_ptr is deprecated
+ // CHECK-FIXES: typedef std::unique_ptr<int> int_ptr_type;
+};
+
+
+// FIXME: Test template WITH instantiation.
+template <typename T> struct B {
+ typedef typename std::auto_ptr<T> created_type;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: auto_ptr is deprecated
+ // CHECK-FIXES: typedef typename std::unique_ptr<T> created_type;
+
+ created_type create() { return std::auto_ptr<T>(new T()); }
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: auto_ptr is deprecated
+ // CHECK-FIXES: created_type create() { return std::unique_ptr<T>(new T()); }
+};
+
+
+// Test 'using' in a namespace (declaration)
+namespace ns_1 {
+// Test multiple using declarations.
+ using std::auto_ptr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+ // CHECK-FIXES: using std::unique_ptr;
+ using std::auto_ptr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+ // CHECK-FIXES: using std::unique_ptr;
+}
+
+
+namespace ns_2 {
+template <typename T> struct auto_ptr {};
+}
+
+void f_1() {
+ std::auto_ptr<int> a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> a;
+
+ // Check that spaces aren't modified unnecessarily.
+ std:: auto_ptr <int> b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std:: unique_ptr <int> b;
+ std :: auto_ptr < char > c(new char());
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std :: unique_ptr < char > c(new char());
+
+ // Test construction from a temporary.
+ std::auto_ptr<char> d = std::auto_ptr<char>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<char> d = std::unique_ptr<char>();
+
+ typedef std::auto_ptr<int> int_ptr_t;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: auto_ptr is deprecated
+ // CHECK-FIXES: typedef std::unique_ptr<int> int_ptr_t;
+ int_ptr_t e(new int());
+
+ // Test pointers.
+ std::auto_ptr<int> *f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> *f;
+
+ // Test 'static' declarations.
+ static std::auto_ptr<int> g;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+ // CHECK-FIXES: static std::unique_ptr<int> g;
+
+ // Test with cv-qualifiers.
+ const std::auto_ptr<int> h;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+ // CHECK-FIXES: const std::unique_ptr<int> h;
+ volatile std::auto_ptr<int> i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+ // CHECK-FIXES: volatile std::unique_ptr<int> i;
+ const volatile std::auto_ptr<int> j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: auto_ptr is deprecated
+ // CHECK-FIXES: const volatile std::unique_ptr<int> j;
+
+ // Test auto and initializer-list.
+ auto k = std::auto_ptr<int>{};
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+ // CHECK-FIXES: auto k = std::unique_ptr<int>{};
+ std::auto_ptr<int> l{std::auto_ptr<int>()};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> l{std::unique_ptr<int>()};
+
+ // Test interlocked auto_ptr.
+ std::auto_ptr<std::auto_ptr<int> > m;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<std::unique_ptr<int> > m;
+
+ // Test temporaries.
+ std::auto_ptr<char>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<char>();
+
+ // Test void-specialization.
+ std::auto_ptr<void> n;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<void> n;
+
+ // Test template WITH instantiation (instantiation).
+ B<double> o;
+ std::auto_ptr<double> p(o.create());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<double> p(o.create());
+
+ // Test 'using' in a namespace ("definition").
+ ns_1::auto_ptr<int> q;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: auto_ptr is deprecated
+ // CHECK-FIXES: ns_1::unique_ptr<int> q;
+
+ // Test construction with an 'auto_ptr_ref'.
+ std::auto_ptr<Base> r(create_derived_ptr());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<Base> r(create_derived_ptr());
+}
+
+// Test without the nested name specifiers.
+void f_2() {
+ using namespace std;
+
+ auto_ptr<int> a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: auto_ptr is deprecated
+ // CHECK-FIXES: unique_ptr<int> a;
+}
+
+// Test using declaration.
+void f_3() {
+ using std::auto_ptr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+ // CHECK-FIXES: using std::unique_ptr;
+
+ auto_ptr<int> a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: auto_ptr is deprecated
+ // CHECK-FIXES: unique_ptr<int> a;
+}
+
+// Test messing-up with macros.
+void f_4() {
+#define MACRO_1 <char>
+ std::auto_ptr MACRO_1 p(new char());
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr MACRO_1 p(new char());
+#define MACRO_2 auto_ptr
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+ // CHECK-FIXES: #define MACRO_2 unique_ptr
+ std::MACRO_2<int> q;
+#define MACRO_3(Type) std::auto_ptr<Type>
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: auto_ptr is deprecated
+ // CHECK-FIXES: #define MACRO_3(Type) std::unique_ptr<Type>
+ MACRO_3(float)r(new float());
+#define MACRO_4 std::auto_ptr
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: auto_ptr is deprecated
+ // CHECK-FIXES: #define MACRO_4 std::unique_ptr
+ using MACRO_4;
+#undef MACRO_1
+#undef MACRO_2
+#undef MACRO_3
+#undef MACRO_4
+}
+
+// Test function return values (definition).
+std::auto_ptr<char> f_5()
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<char> f_5()
+{
+ // Test constructor.
+ return std::auto_ptr<char>(new char());
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+ // CHECK-FIXES: return std::unique_ptr<char>(new char());
+}
+
+// Test that non-std auto_ptr aren't replaced.
+void f_8() {
+ ns_2::auto_ptr<char> a;
+ using namespace ns_2;
+ auto_ptr<int> b;
+}
+
+// Fail to modify when the template is never instantiated.
+//
+// This might not be an issue. If it's never used it doesn't really matter if
+// it's changed or not. If it's a header and one of the source use it, then it
+// will still be changed.
+template <typename X>
+void f() {
+ std::auto_ptr<X> p;
+}
+
+// FIXME: Alias template could be replaced if a matcher existed.
+namespace std {
+template <typename T> using aaaaaaaa = auto_ptr<T>;
+}
+
+// We want to avoid replacing 'aaaaaaaa' by unique_ptr here. It's better to
+// change the type alias directly.
+std::aaaaaaaa<int> d;
+
+
+void takes_ownership_fn(std::auto_ptr<int> x);
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: auto_ptr is deprecated
+// CHECK-FIXES: void takes_ownership_fn(std::unique_ptr<int> x);
+
+std::auto_ptr<int> get_by_value();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+// CHECK-FIXES: std::unique_ptr<int> get_by_value();
+
+class Wrapper {
+ public:
+ std::auto_ptr<int> &get_wrapped();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+
+ private:
+ std::auto_ptr<int> wrapped;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+};
+
+void f() {
+ std::auto_ptr<int> a, b, c;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> a, b, c;
+ Wrapper wrapper_a, wrapper_b;
+
+ a = b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::move to transfer ownership
+ // CHECK-FIXES: a = std::move(b);
+
+ wrapper_a.get_wrapped() = wrapper_b.get_wrapped();
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::move to transfer ownership
+ // CHECK-FIXES: wrapper_a.get_wrapped() = std::move(wrapper_b.get_wrapped());
+
+ // Test that 'std::move()' is inserted when call to the
+ // copy-constructor are made.
+ takes_ownership_fn(c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::move to transfer ownership
+ // CHECK-FIXES: takes_ownership_fn(std::move(c));
+ takes_ownership_fn(wrapper_a.get_wrapped());
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::move to transfer ownership
+ // CHECK-FIXES: takes_ownership_fn(std::move(wrapper_a.get_wrapped()));
+
+ std::auto_ptr<int> d[] = { std::auto_ptr<int>(new int(1)),
+ std::auto_ptr<int>(new int(2)) };
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-3]]:35: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-3]]:35: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> d[] = { std::unique_ptr<int>(new int(1)),
+ // CHECK-FIXES-NEXT: std::unique_ptr<int>(new int(2)) };
+ std::auto_ptr<int> e = d[0];
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: use std::move to transfer ownership
+ // CHECK: std::unique_ptr<int> e = std::move(d[0]);
+
+ // Test that std::move() is not used when assigning an rvalue
+ std::auto_ptr<int> f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> f;
+ f = std::auto_ptr<int>(new int(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: auto_ptr is deprecated
+ // CHECK-NEXT: f = std::unique_ptr<int>(new int(0));
+
+ std::auto_ptr<int> g = get_by_value();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+ // CHECK-FIXES: std::unique_ptr<int> g = get_by_value();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-replace-random-shuffle %t -- -- -std=c++11
+
+//CHECK-FIXES: #include <random>
+
+namespace std {
+template <typename T> struct vec_iterator {
+ T *ptr;
+ vec_iterator operator++(int);
+};
+
+template <typename T> struct vector {
+ typedef vec_iterator<T> iterator;
+
+ iterator begin();
+ iterator end();
+};
+
+template <typename FwIt>
+void random_shuffle(FwIt begin, FwIt end);
+
+template <typename FwIt, typename randomFunc>
+void random_shuffle(FwIt begin, FwIt end, randomFunc& randomfunc);
+
+template <typename FwIt>
+void shuffle(FwIt begin, FwIt end);
+} // namespace std
+
+// Random Func
+int myrandom (int i) { return i;}
+
+using namespace std;
+
+int main() {
+ std::vector<int> vec;
+
+ std::random_shuffle(vec.begin(), vec.end());
+ // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' instead
+ // CHECK-FIXES: std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+ std::shuffle(vec.begin(), vec.end());
+
+ random_shuffle(vec.begin(), vec.end());
+ // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' instead
+ // CHECK-FIXES: shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+ std::random_shuffle(vec.begin(), vec.end(), myrandom);
+ // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' and an alternative random mechanism instead
+ // CHECK-FIXES: std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+ random_shuffle(vec.begin(), vec.end(), myrandom);
+ // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' and an alternative random mechanism instead
+ // CHECK-FIXES: shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+ shuffle(vec.begin(), vec.end());
+
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-return-braced-init-list %t -- -- -std=c++14
+
+namespace std {
+typedef decltype(sizeof(int)) size_t;
+
+// libc++'s implementation
+template <class _E>
+class initializer_list {
+ const _E *__begin_;
+ size_t __size_;
+
+ initializer_list(const _E *__b, size_t __s)
+ : __begin_(__b),
+ __size_(__s) {}
+
+public:
+ typedef _E value_type;
+ typedef const _E &reference;
+ typedef const _E &const_reference;
+ typedef size_t size_type;
+
+ typedef const _E *iterator;
+ typedef const _E *const_iterator;
+
+ initializer_list() : __begin_(nullptr), __size_(0) {}
+
+ size_t size() const { return __size_; }
+ const _E *begin() const { return __begin_; }
+ const _E *end() const { return __begin_ + __size_; }
+};
+
+template <typename T>
+class vector {
+public:
+ vector(T) {}
+ vector(std::initializer_list<T>) {}
+};
+}
+
+class Bar {};
+
+Bar b0;
+
+class Foo {
+public:
+ Foo(Bar) {}
+ explicit Foo(Bar, unsigned int) {}
+ Foo(unsigned int) {}
+};
+
+class Baz {
+public:
+ Foo m() {
+ Bar bm;
+ return Foo(bm);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid repeating the return type from the declaration; use a braced initializer list instead [modernize-return-braced-init-list]
+ // CHECK-FIXES: return {bm};
+ }
+};
+
+class Quux : public Foo {
+public:
+ Quux(Bar bar) : Foo(bar) {}
+ Quux(unsigned, unsigned, unsigned k = 0) : Foo(k) {}
+};
+
+Foo f() {
+ Bar b1;
+ return Foo(b1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {b1};
+}
+
+Foo f2() {
+ Bar b2;
+ return {b2};
+}
+
+auto f3() {
+ Bar b3;
+ return Foo(b3);
+}
+
+#define A(b) Foo(b)
+
+Foo f4() {
+ Bar b4;
+ return A(b4);
+}
+
+Foo f5() {
+ Bar b5;
+ return Quux(b5);
+}
+
+Foo f6() {
+ Bar b6;
+ return Foo(b6, 1);
+}
+
+std::vector<int> f7() {
+ int i7 = 1;
+ return std::vector<int>(i7);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+}
+
+Bar f8() {
+ return {};
+}
+
+Bar f9() {
+ return Bar();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+}
+
+Bar f10() {
+ return Bar{};
+}
+
+Foo f11(Bar b11) {
+ return Foo(b11);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {b11};
+}
+
+Foo f12() {
+ return f11(Bar());
+}
+
+Foo f13() {
+ return Foo(Bar()); // 13
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {Bar()}; // 13
+}
+
+Foo f14() {
+ // FIXME: Type narrowing should not occur!
+ return Foo(-1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {-1};
+}
+
+Foo f15() {
+ return Foo(f10());
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {f10()};
+}
+
+Quux f16() {
+ return Quux(1, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {1, 2};
+}
+
+Quux f17() {
+ return Quux(1, 2, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+ // CHECK-FIXES: return {1, 2, 3};
+}
+
+template <typename T>
+T f19() {
+ return T();
+}
+
+Bar i1 = f19<Bar>();
+Baz i2 = f19<Baz>();
+
+template <typename T>
+Foo f20(T t) {
+ return Foo(t);
+}
+
+Foo i3 = f20(b0);
+
+template <typename T>
+class BazT {
+public:
+ T m() {
+ Bar b;
+ return T(b);
+ }
+
+ Foo m2(T t) {
+ return Foo(t);
+ }
+};
+
+BazT<Foo> bazFoo;
+Foo i4 = bazFoo.m();
+Foo i5 = bazFoo.m2(b0);
+
+BazT<Quux> bazQuux;
+Foo i6 = bazQuux.m();
+Foo i7 = bazQuux.m2(b0);
+
+auto v1 = []() { return std::vector<int>({1, 2}); }();
+auto v2 = []() -> std::vector<int> { return std::vector<int>({1, 2}); }();
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-shrink-to-fit %t
+
+namespace std {
+template <typename T> struct vector { void swap(vector &other); };
+}
+
+void f() {
+ std::vector<int> v;
+
+ std::vector<int>(v).swap(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should be used to reduce the capacity of a shrinkable container [modernize-shrink-to-fit]
+ // CHECK-FIXES: {{^ }}v.shrink_to_fit();{{$}}
+
+ std::vector<int> &vref = v;
+ std::vector<int>(vref).swap(vref);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}vref.shrink_to_fit();{{$}}
+
+ std::vector<int> *vptr = &v;
+ std::vector<int>(*vptr).swap(*vptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}vptr->shrink_to_fit();{{$}}
+}
+
+struct X {
+ std::vector<int> v;
+ void f() {
+ std::vector<int>(v).swap(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}v.shrink_to_fit();{{$}}
+
+ std::vector<int> *vptr = &v;
+ std::vector<int>(*vptr).swap(*vptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}vptr->shrink_to_fit();{{$}}
+ }
+};
+
+template <typename T> void g() {
+ std::vector<int> v;
+ std::vector<int>(v).swap(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}v.shrink_to_fit();{{$}}
+
+ std::vector<T> v2;
+ std::vector<T>(v2).swap(v2);
+ // CHECK-FIXES: {{^ }}std::vector<T>(v2).swap(v2);{{$}}
+}
+
+template <typename T> void g2() {
+ std::vector<int> v;
+ std::vector<int>(v).swap(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}v.shrink_to_fit();{{$}}
+
+ T v3;
+ T(v3).swap(v3);
+ // CHECK-FIXES: {{^ }}T(v3).swap(v3);{{$}}
+}
+
+#define COPY_AND_SWAP_INT_VEC(x) std::vector<int>(x).swap(x)
+// CHECK-FIXES: #define COPY_AND_SWAP_INT_VEC(x) std::vector<int>(x).swap(x)
+
+void h() {
+ g<int>();
+ g<double>();
+ g<bool>();
+ g2<std::vector<int>>();
+ std::vector<int> v;
+ COPY_AND_SWAP_INT_VEC(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+ // CHECK-FIXES: {{^ }}COPY_AND_SWAP_INT_VEC(v);{{$}}
+}
+
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-unary-static-assert %t -- -- -std=c++1z
+
+#define FOO static_assert(sizeof(a) <= 15, "");
+#define MSG ""
+
+void f_textless(int a) {
+ static_assert(sizeof(a) <= 10, "");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use unary 'static_assert' when the string literal is an empty string [modernize-unary-static-assert]
+ // CHECK-FIXES: {{^}} static_assert(sizeof(a) <= 10 );{{$}}
+ static_assert(sizeof(a) <= 12, L"");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use unary 'static_assert' when
+ // CHECK-FIXES: {{^}} static_assert(sizeof(a) <= 12 );{{$}}
+ FOO
+ // CHECK-FIXES: {{^}} FOO{{$}}
+ static_assert(sizeof(a) <= 17, MSG);
+ // CHECK-FIXES: {{^}} static_assert(sizeof(a) <= 17, MSG);{{$}}
+}
+
+void f_with_tex(int a) {
+ static_assert(sizeof(a) <= 10, "Size of variable a is out of range!");
+}
+
+void f_unary(int a) { static_assert(sizeof(a) <= 10); }
+
+void f_incorrect_assert() { static_assert(""); }
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
+// RUN: -- -std=c++11 -frtti
+
+struct A {
+ virtual ~A() {}
+};
+
+struct B : public A {};
+
+struct C {};
+
+void f_static_cast() {
+ long l = 1;
+ int i1 = static_cast<int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = static_cast<int>(l);
+
+ const int i2 = static_cast<int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto i2 = static_cast<int>(l);
+
+ long long ll = static_cast<long long>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ll = static_cast<long long>(l);
+ unsigned long long ull = static_cast<unsigned long long>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ull = static_cast<unsigned long long>(l);
+ unsigned int ui = static_cast<unsigned int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ui = static_cast<unsigned int>(l);
+ long double ld = static_cast<long double>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ld = static_cast<long double>(l);
+
+ A *a = new B();
+ B *b1 = static_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b1 = static_cast<B *>(a);
+
+ B *const b2 = static_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto const b2 = static_cast<B *>(a);
+
+ const B *b3 = static_cast<const B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto b3 = static_cast<const B *>(a);
+
+ B &b4 = static_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b4 = static_cast<B &>(*a);
+
+ const B &b5 = static_cast<const B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto &b5 = static_cast<const B &>(*a);
+
+ B &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+
+ // Don't warn when non-cast involved
+ long double cast = static_cast<long double>(l), noncast = 5;
+
+ // Don't warn when auto is already being used.
+ auto i3 = static_cast<int>(l);
+ auto *b8 = static_cast<B *>(a);
+ auto &b9 = static_cast<B &>(*a);
+}
+
+void f_dynamic_cast() {
+ A *a = new B();
+ B *b1 = dynamic_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b1 = dynamic_cast<B *>(a);
+
+ B &b2 = dynamic_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b2 = dynamic_cast<B &>(*a);
+}
+
+void f_reinterpret_cast() {
+ auto *a = new A();
+ C *c1 = reinterpret_cast<C *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto c1 = reinterpret_cast<C *>(a);
+
+ C &c2 = reinterpret_cast<C &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &c2 = reinterpret_cast<C &>(*a);
+}
+
+void f_const_cast() {
+ const A *a1 = new A();
+ A *a2 = const_cast<A *>(a1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto a2 = const_cast<A *>(a1);
+ A &a3 = const_cast<A &>(*a1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &a3 = const_cast<A &>(*a1);
+}
+
+typedef unsigned char xmlChar;
+#define BAD_CAST (xmlChar *)
+
+#define XMLCHAR_CAST(x) (xmlChar *)(x)
+
+#define CAST_IN_MACRO(x) \
+ do { \
+ xmlChar *s = (xmlChar *)(x); \
+ } while (false);
+// CHECK-FIXES: xmlChar *s = (xmlChar *)(x);
+
+void f_cstyle_cast() {
+ auto *a = new A();
+ C *c1 = (C *)a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto c1 = (C *)a;
+
+ C &c2 = (C &)*a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &c2 = (C &)*a;
+
+ xmlChar *s = BAD_CAST "xml";
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto s = BAD_CAST "xml";
+ xmlChar *t = XMLCHAR_CAST("xml");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto t = XMLCHAR_CAST("xml");
+ CAST_IN_MACRO("xml");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+}
+
+void f_functional_cast() {
+ long l = 1;
+ int i1 = int(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = int(l);
+
+ B b;
+ A a = A(b);
+}
+
+class StringRef
+{
+public:
+ StringRef(const char *);
+ const char *begin() const;
+ const char *end() const;
+};
+
+template <typename T, typename U>
+T template_value_cast(const U &u);
+
+template <typename T, typename U>
+T *template_pointer_cast(U *u);
+
+template <typename T, typename U>
+T &template_reference_cast(U &u);
+
+template <typename T, typename U>
+const T *template_const_pointer_cast(const U *u);
+
+template <typename T, typename U>
+const T &template_const_reference_cast(const U &u);
+
+template <typename T>
+T template_value_get(StringRef s);
+
+struct S {
+ template <typename T>
+ const T *template_member_get();
+};
+
+template <typename T>
+T max(T t1, T t2);
+
+void f_template_cast()
+{
+ double d = 0;
+ int i1 = template_value_cast<int>(d);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = template_value_cast<int>(d);
+
+ A *a = new B();
+ B *b1 = template_value_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b1 = template_value_cast<B *>(a);
+ B &b2 = template_value_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b2 = template_value_cast<B &>(*a);
+ B *b3 = template_pointer_cast<B>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b3 = template_pointer_cast<B>(a);
+ B &b4 = template_reference_cast<B>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b4 = template_reference_cast<B>(*a);
+ const B *b5 = template_const_pointer_cast<B>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto b5 = template_const_pointer_cast<B>(a);
+ const B &b6 = template_const_reference_cast<B>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto &b6 = template_const_reference_cast<B>(*a);
+ B *b7 = template_value_get<B *>("foo");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b7 = template_value_get<B *>("foo");
+ B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto b8 = template_value_get<B *>("foo"), b9 = template_value_get<B *>("bar");
+
+ S s;
+ const B *b10 = s.template_member_get<B>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto b10 = s.template_member_get<B>();
+
+ // Don't warn when auto is already being used.
+ auto i2 = template_value_cast<int>(d);
+ auto *i3 = template_value_cast<int *>(d);
+ auto **i4 = template_value_cast<int **>(d);
+ auto &i5 = template_reference_cast<int>(d);
+
+ // Don't warn for implicit template arguments.
+ int i6 = max(i1, i2);
+
+ // Don't warn for mismatched var and initializer types.
+ A *a1 = template_value_cast<B *>(a);
+
+ // Don't warn for mismatched var types.
+ B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");
+
+ // Don't warn for implicit variables.
+ for (auto &c : template_reference_cast<StringRef>(*a)) {
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- -- \
+// RUN: -std=c++11 -I %S/Inputs/modernize-use-auto -frtti
+
+struct A {
+ virtual ~A() {}
+};
+
+struct B : public A {};
+
+struct C {};
+
+void f_static_cast() {
+ long l = 1;
+ int i1 = static_cast<int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = static_cast<int>(l);
+
+ const int i2 = static_cast<int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto i2 = static_cast<int>(l);
+
+ long long ll = static_cast<long long>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ll = static_cast<long long>(l);
+ unsigned long long ull = static_cast<unsigned long long>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ull = static_cast<unsigned long long>(l);
+ unsigned int ui = static_cast<unsigned int>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ui = static_cast<unsigned int>(l);
+ long double ld = static_cast<long double>(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto ld = static_cast<long double>(l);
+
+ A *a = new B();
+ B *b1 = static_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b1 = static_cast<B *>(a);
+
+ B *const b2 = static_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *const b2 = static_cast<B *>(a);
+
+ const B *b3 = static_cast<const B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto *b3 = static_cast<const B *>(a);
+
+ B &b4 = static_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b4 = static_cast<B &>(*a);
+
+ const B &b5 = static_cast<const B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto &b5 = static_cast<const B &>(*a);
+
+ B &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+
+ // Don't warn when non-cast involved
+ long double cast = static_cast<long double>(l), noncast = 5;
+
+ // Don't warn when auto is already being used.
+ auto i3 = static_cast<int>(l);
+ auto *b8 = static_cast<B *>(a);
+ auto &b9 = static_cast<B &>(*a);
+}
+
+void f_dynamic_cast() {
+ A *a = new B();
+ B *b1 = dynamic_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b1 = dynamic_cast<B *>(a);
+
+ B &b2 = dynamic_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b2 = dynamic_cast<B &>(*a);
+}
+
+void f_reinterpret_cast() {
+ auto *a = new A();
+ C *c1 = reinterpret_cast<C *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *c1 = reinterpret_cast<C *>(a);
+
+ C &c2 = reinterpret_cast<C &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &c2 = reinterpret_cast<C &>(*a);
+}
+
+void f_const_cast() {
+ const A *a1 = new A();
+ A *a2 = const_cast<A *>(a1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *a2 = const_cast<A *>(a1);
+ A &a3 = const_cast<A &>(*a1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &a3 = const_cast<A &>(*a1);
+}
+
+typedef unsigned char xmlChar;
+#define BAD_CAST (xmlChar *)
+
+#define XMLCHAR_CAST(x) (xmlChar *)(x)
+
+#define CAST_IN_MACRO(x) \
+ do { \
+ xmlChar *s = (xmlChar *)(x); \
+ } while (false);
+// CHECK-FIXES: xmlChar *s = (xmlChar *)(x);
+
+void f_cstyle_cast() {
+ auto *a = new A();
+ C *c1 = (C *)a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *c1 = (C *)a;
+
+ C &c2 = (C &)*a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &c2 = (C &)*a;
+
+ xmlChar *s = BAD_CAST "xml";
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *s = BAD_CAST "xml";
+ xmlChar *t = XMLCHAR_CAST("xml");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *t = XMLCHAR_CAST("xml");
+ CAST_IN_MACRO("xml");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+}
+
+void f_functional_cast() {
+ long l = 1;
+ int i1 = int(l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = int(l);
+
+ B b;
+ A a = A(b);
+}
+
+class StringRef
+{
+public:
+ StringRef(const char *);
+ const char *begin() const;
+ const char *end() const;
+};
+
+template <typename T, typename U>
+T template_value_cast(const U &u);
+
+template <typename T, typename U>
+T *template_pointer_cast(U *u);
+
+template <typename T, typename U>
+T &template_reference_cast(U &u);
+
+template <typename T, typename U>
+const T *template_const_pointer_cast(const U *u);
+
+template <typename T, typename U>
+const T &template_const_reference_cast(const U &u);
+
+template <typename T>
+T template_value_get(StringRef s);
+
+struct S {
+ template <typename T>
+ const T *template_member_get();
+};
+
+template <typename T>
+T max(T t1, T t2);
+
+void f_template_cast()
+{
+ double d = 0;
+ int i1 = template_value_cast<int>(d);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto i1 = template_value_cast<int>(d);
+
+ A *a = new B();
+ B *b1 = template_value_cast<B *>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b1 = template_value_cast<B *>(a);
+ B &b2 = template_value_cast<B &>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b2 = template_value_cast<B &>(*a);
+ B *b3 = template_pointer_cast<B>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b3 = template_pointer_cast<B>(a);
+ B &b4 = template_reference_cast<B>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto &b4 = template_reference_cast<B>(*a);
+ const B *b5 = template_const_pointer_cast<B>(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto *b5 = template_const_pointer_cast<B>(a);
+ const B &b6 = template_const_reference_cast<B>(*a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto &b6 = template_const_reference_cast<B>(*a);
+ B *b7 = template_value_get<B *>("foo");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b7 = template_value_get<B *>("foo");
+ B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: auto *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+
+ S s;
+ const B *b10 = s.template_member_get<B>();
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+ // CHECK-FIXES: const auto *b10 = s.template_member_get<B>();
+
+ // Don't warn when auto is already being used.
+ auto i2 = template_value_cast<int>(d);
+ auto *i3 = template_value_cast<int *>(d);
+ auto **i4 = template_value_cast<int **>(d);
+ auto &i5 = template_reference_cast<int>(d);
+
+ // Don't warn for implicit template arguments.
+ int i6 = max(i1, i2);
+
+ // Don't warn for mismatched var and initializer types.
+ A *a1 = template_value_cast<B *>(a);
+
+ // Don't warn for mismatched var types.
+ B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");
+
+ // Don't warn for implicit variables.
+ for (auto &c : template_reference_cast<StringRef>(*a)) {
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- -- \
+// RUN: -std=c++11 -I %S/Inputs/modernize-use-auto
+
+#include "containers.h"
+
+void f_array() {
+ std::array<int, 4> C;
+ std::array<int, 4>::iterator ArrayI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators [modernize-use-auto]
+ // CHECK-FIXES: auto ArrayI1 = C.begin();
+
+ std::array<int, 5>::reverse_iterator ArrayI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ArrayI2 = C.rbegin();
+
+ const std::array<int, 3> D;
+ std::array<int, 3>::const_iterator ArrayI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ArrayI3 = D.begin();
+
+ std::array<int, 5>::const_reverse_iterator ArrayI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ArrayI4 = D.rbegin();
+}
+
+void f_deque() {
+ std::deque<int> C;
+ std::deque<int>::iterator DequeI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto DequeI1 = C.begin();
+
+ std::deque<int>::reverse_iterator DequeI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto DequeI2 = C.rbegin();
+
+ const std::deque<int> D;
+ std::deque<int>::const_iterator DequeI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto DequeI3 = D.begin();
+
+ std::deque<int>::const_reverse_iterator DequeI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto DequeI4 = D.rbegin();
+}
+
+void f_forward_list() {
+ std::forward_list<int> C;
+ std::forward_list<int>::iterator FListI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto FListI1 = C.begin();
+
+ const std::forward_list<int> D;
+ std::forward_list<int>::const_iterator FListI2 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto FListI2 = D.begin();
+}
+
+void f_list() {
+ std::list<int> C;
+ std::list<int>::iterator ListI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ListI1 = C.begin();
+ std::list<int>::reverse_iterator ListI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ListI2 = C.rbegin();
+
+ const std::list<int> D;
+ std::list<int>::const_iterator ListI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ListI3 = D.begin();
+ std::list<int>::const_reverse_iterator ListI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto ListI4 = D.rbegin();
+}
+
+void f_vector() {
+ std::vector<int> C;
+ std::vector<int>::iterator VecI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto VecI1 = C.begin();
+
+ std::vector<int>::reverse_iterator VecI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto VecI2 = C.rbegin();
+
+ const std::vector<int> D;
+ std::vector<int>::const_iterator VecI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto VecI3 = D.begin();
+
+ std::vector<int>::const_reverse_iterator VecI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto VecI4 = D.rbegin();
+}
+
+void f_map() {
+ std::map<int, int> C;
+ std::map<int, int>::iterator MapI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MapI1 = C.begin();
+
+ std::map<int, int>::reverse_iterator MapI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MapI2 = C.rbegin();
+
+ const std::map<int, int> D;
+ std::map<int, int>::const_iterator MapI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MapI3 = D.begin();
+
+ std::map<int, int>::const_reverse_iterator MapI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MapI4 = D.rbegin();
+}
+
+void f_multimap() {
+ std::multimap<int, int> C;
+ std::multimap<int, int>::iterator MMapI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MMapI1 = C.begin();
+
+ std::multimap<int, int>::reverse_iterator MMapI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MMapI2 = C.rbegin();
+
+ const std::multimap<int, int> D;
+ std::multimap<int, int>::const_iterator MMapI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MMapI3 = D.begin();
+
+ std::multimap<int, int>::const_reverse_iterator MMapI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MMapI4 = D.rbegin();
+}
+
+void f_set() {
+ std::set<int> C;
+ std::set<int>::iterator SetI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto SetI1 = C.begin();
+
+ std::set<int>::reverse_iterator SetI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto SetI2 = C.rbegin();
+
+ const std::set<int> D;
+ std::set<int>::const_iterator SetI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto SetI3 = D.begin();
+
+ std::set<int>::const_reverse_iterator SetI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto SetI4 = D.rbegin();
+}
+
+void f_multiset() {
+ std::multiset<int> C;
+ std::multiset<int>::iterator MSetI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MSetI1 = C.begin();
+
+ std::multiset<int>::reverse_iterator MSetI2 = C.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MSetI2 = C.rbegin();
+
+ const std::multiset<int> D;
+ std::multiset<int>::const_iterator MSetI3 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MSetI3 = D.begin();
+
+ std::multiset<int>::const_reverse_iterator MSetI4 = D.rbegin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto MSetI4 = D.rbegin();
+}
+
+void f_unordered_map() {
+ std::unordered_map<int, int> C;
+ std::unordered_map<int, int>::iterator UMapI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMapI1 = C.begin();
+
+ const std::unordered_map<int, int> D;
+ std::unordered_map<int, int>::const_iterator UMapI2 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMapI2 = D.begin();
+}
+
+void f_unordered_multimap() {
+ std::unordered_multimap<int, int> C;
+ std::unordered_multimap<int, int>::iterator UMMapI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMMapI1 = C.begin();
+
+ const std::unordered_multimap<int, int> D;
+ std::unordered_multimap<int, int>::const_iterator UMMapI2 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMMapI2 = D.begin();
+}
+
+void f_unordered_set() {
+ std::unordered_set<int> C;
+ std::unordered_set<int>::iterator USetI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto USetI1 = C.begin();
+
+ const std::unordered_set<int> D;
+ std::unordered_set<int>::const_iterator USetI2 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto USetI2 = D.begin();
+}
+
+void f_unordered_multiset() {
+ std::unordered_multiset<int> C;
+ std::unordered_multiset<int>::iterator UMSetI1 = C.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMSetI1 = C.begin();
+
+ const std::unordered_multiset<int> D;
+ std::unordered_multiset<int>::const_iterator UMSetI2 = D.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto UMSetI2 = D.begin();
+}
+
+typedef std::vector<int>::iterator int_iterator;
+
+std::vector<int> Vec;
+std::unordered_map<int, int> Map;
+
+void sugar() {
+ // Types with more sugar should work. Types with less should not.
+ int_iterator more_sugar = Vec.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto more_sugar = Vec.begin();
+}
+
+void initializer_list() {
+ // Initialization from initializer lists isn't allowed. Using 'auto' would
+ // result in std::initializer_list being deduced for the type.
+ std::unordered_map<int, int>::iterator I{Map.begin()};
+ std::unordered_map<int, int>::iterator I2 = {Map.begin()};
+}
+
+void construction() {
+ // Various forms of construction. Default constructors and constructors with
+ // all-default parameters shouldn't get transformed. Construction from other
+ // types is also not allowed.
+
+ std::unordered_map<int, int>::iterator copy(Map.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto copy(Map.begin());
+
+ std::unordered_map<int, int>::iterator def;
+ std::unordered_map<int, int>::const_iterator constI;
+
+ // Implicit conversion.
+ std::unordered_map<int, int>::const_iterator constI2 = def;
+ std::unordered_map<int, int>::const_iterator constI3(def);
+
+ // Explicit conversion
+ std::unordered_map<int, int>::const_iterator constI4
+ = std::unordered_map<int, int>::const_iterator(def);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto constI4
+ // CHECK-FIXES-NEXT: = std::unordered_map<int, int>::const_iterator(def);
+}
+
+void pointer_to_iterator() {
+ int_iterator I = Vec.begin();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto I = Vec.begin();
+
+ // Pointers and references to iterators are not transformed.
+ int_iterator *IPtr = &I;
+ int_iterator &IRef = I;
+}
+
+void loop() {
+ for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+ // CHECK-FIXES: for (auto I = Vec.begin(); I != Vec.end(); ++I)
+ }
+
+ for (int_iterator I = Vec.begin(), E = Vec.end(); I != E; ++I) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+ // CHECK-FIXES: for (auto I = Vec.begin(), E = Vec.end(); I != E; ++I)
+ }
+
+ std::vector<std::vector<int>::iterator> IterVec;
+ for (std::vector<int>::iterator I : IterVec) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+ // CHECK-FIXES: for (auto I : IterVec)
+ }
+}
+
+void cv_qualifiers() {
+ // Make sure references and cv qualifiers don't get removed (i.e. replaced
+ // with just 'auto').
+ const auto & I = Vec.begin();
+ auto && I2 = Vec.begin();
+}
+
+void cleanup() {
+ // Passing a string as an argument to introduce a temporary object that will
+ // create an expression with cleanups.
+ std::map<std::string, int> MapFind;
+ std::map<std::string, int>::iterator I = MapFind.find("foo");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto I = MapFind.find("foo");
+}
+
+void declaration_lists() {
+ // Declaration lists that match the declaration type with written no-list
+ // initializer are transformed.
+ std::vector<int>::iterator I = Vec.begin(), E = Vec.end();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+ // CHECK-FIXES: auto I = Vec.begin(), E = Vec.end();
+
+ // Declaration lists with non-initialized variables should not be transformed.
+ std::vector<int>::iterator J = Vec.begin(), K;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
+// RUN: -- -std=c++11
+
+class MyType {};
+
+class MyDerivedType : public MyType {};
+
+// FIXME: the replacement sometimes results in two consecutive spaces after
+// the word 'auto' (due to the presence of spaces at both sides of '*').
+void auto_new() {
+ MyType *a_new = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto a_new = new MyType();
+
+ static MyType *a_static = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+ // CHECK-FIXES: static auto a_static = new MyType();
+
+ MyType *derived = new MyDerivedType();
+
+ void *vd = new MyType();
+
+ // CV-qualifier tests.
+ //
+ // NOTE : the form "type const" is expected here because of a deficiency in
+ // TypeLoc where CV qualifiers are not considered part of the type location
+ // info. That is, all that is being replaced in each case is "MyType *" and
+ // not "MyType * const".
+ static MyType * const d_static = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+ // CHECK-FIXES: static auto const d_static = new MyType();
+
+ MyType * const a_const = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto const a_const = new MyType();
+
+ MyType * volatile vol = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto volatile vol = new MyType();
+
+ struct SType {} *stype = new SType;
+
+ int (**func)(int, int) = new (int(*[5])(int,int));
+
+ int *array = new int[5];
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto array = new int[5];
+
+ MyType *ptr(new MyType);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto ptr(new MyType);
+
+ MyType *ptr2{new MyType};
+
+ {
+ // Test for declaration lists.
+ MyType *a = new MyType(), *b = new MyType(), *c = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto a = new MyType(), b = new MyType(), c = new MyType();
+
+ // Non-initialized declaration should not be transformed.
+ MyType *d = new MyType(), *e;
+
+ MyType **f = new MyType*(), **g = new MyType*();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto f = new MyType*(), g = new MyType*();
+
+ // Mismatching types in declaration lists should not be transformed.
+ MyType *h = new MyType(), **i = new MyType*();
+
+ // '*' shouldn't be removed in case of mismatching types with multiple
+ // declarations.
+ MyType *j = new MyType(), *k = new MyType(), **l = new MyType*();
+ }
+
+ {
+ // Test for typedefs.
+ typedef int * int_p;
+ // CHECK-FIXES: typedef int * int_p;
+
+ int_p a = new int;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto a = new int;
+ int_p *b = new int*;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto b = new int*;
+
+ // Test for typedefs in declarations lists.
+ int_p c = new int, d = new int;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto c = new int, d = new int;
+
+ // Different types should not be transformed.
+ int_p e = new int, *f = new int_p;
+
+ int_p *g = new int*, *h = new int_p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto g = new int*, h = new int_p;
+ }
+
+ // Don't warn when 'auto' is already being used.
+ auto aut = new MyType();
+ auto *paut = new MyType();
+ const auto *pcaut = new MyType();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-auto %t
+
+class MyType {};
+
+class MyDerivedType : public MyType {};
+
+// FIXME: the replacement sometimes results in two consecutive spaces after
+// the word 'auto' (due to the presence of spaces at both sides of '*').
+void auto_new() {
+ MyType *a_new = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *a_new = new MyType();
+
+ static MyType *a_static = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+ // CHECK-FIXES: static auto *a_static = new MyType();
+
+ long long *ll = new long long();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *ll = new long long();
+
+ MyType *derived = new MyDerivedType();
+
+ void *vd = new MyType();
+
+ // CV-qualifier tests.
+ //
+ // NOTE : the form "type const" is expected here because of a deficiency in
+ // TypeLoc where CV qualifiers are not considered part of the type location
+ // info. That is, all that is being replaced in each case is "MyType *" and
+ // not "MyType * const".
+ static MyType * const d_static = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+ // CHECK-FIXES: static auto * const d_static = new MyType();
+
+ MyType * const a_const = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto * const a_const = new MyType();
+
+ MyType * volatile vol = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto * volatile vol = new MyType();
+
+ struct SType {} *stype = new SType;
+
+ int (**func)(int, int) = new (int(*[5])(int,int));
+
+ int *array = new int[5];
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *array = new int[5];
+
+ MyType *ptr(new MyType);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *ptr(new MyType);
+
+ MyType *ptr2{new MyType};
+
+ {
+ // Test for declaration lists.
+ MyType *a = new MyType(), *b = new MyType(), *c = new MyType();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *a = new MyType(), *b = new MyType(), *c = new MyType();
+
+ // Non-initialized declaration should not be transformed.
+ MyType *d = new MyType(), *e;
+
+ MyType **f = new MyType*(), **g = new MyType*();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto **f = new MyType*(), **g = new MyType*();
+
+ // Mismatching types in declaration lists should not be transformed.
+ MyType *h = new MyType(), **i = new MyType*();
+
+ // '*' shouldn't be removed in case of mismatching types with multiple
+ // declarations.
+ MyType *j = new MyType(), *k = new MyType(), **l = new MyType*();
+ }
+
+ {
+ // Test for typedefs.
+ typedef int * int_p;
+ // CHECK-FIXES: typedef int * int_p;
+
+ int_p a = new int;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto a = new int;
+ int_p *b = new int*;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *b = new int*;
+
+ // Test for typedefs in declarations lists.
+ int_p c = new int, d = new int;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto c = new int, d = new int;
+
+ // Different types should not be transformed.
+ int_p e = new int, *f = new int_p;
+
+ int_p *g = new int*, *h = new int_p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+ // CHECK-FIXES: auto *g = new int*, *h = new int_p;
+ }
+
+ // Don't warn when 'auto' is already being used.
+ auto aut = new MyType();
+ auto *paut = new MyType();
+ const auto *pcaut = new MyType();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-bool-literals %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: modernize-use-bool-literals.IgnoreMacros, \
+// RUN: value: 1}]}" \
+// RUN: -- -std=c++11
+
+bool IntToTrue = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: converting integer literal to bool, use bool literal instead [modernize-use-bool-literals]
+// CHECK-FIXES: {{^}}bool IntToTrue = true;{{$}}
+
+bool IntToFalse(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool IntToFalse(false);{{$}}
+
+bool LongLongToTrue{0x1LL};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool LongLongToTrue{true};{{$}}
+
+bool ExplicitCStyleIntToFalse = (bool)0;
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitCStyleIntToFalse = false;{{$}}
+
+bool ExplicitFunctionalIntToFalse = bool(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitFunctionalIntToFalse = false;{{$}}
+
+bool ExplicitStaticIntToFalse = static_cast<bool>(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitStaticIntToFalse = false;{{$}}
+
+#define TRUE_MACRO 1
+// CHECK-FIXES: {{^}}#define TRUE_MACRO 1{{$}}
+
+bool MacroIntToTrue = TRUE_MACRO;
+// CHECK-FIXES: {{^}}bool MacroIntToTrue = TRUE_MACRO;{{$}}
+
+#define FALSE_MACRO bool(0)
+// CHECK-FIXES: {{^}}#define FALSE_MACRO bool(0){{$}}
+
+bool TrueBool = true; // OK
+
+bool FalseBool = bool(FALSE_MACRO);
+// CHECK-FIXES: {{^}}bool FalseBool = bool(FALSE_MACRO);{{$}}
+
+void boolFunction(bool bar) {
+
+}
+
+char Character = 0; // OK
+
+unsigned long long LongInteger = 1; // OK
+
+#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x)
+// CHECK-FIXES: {{^}}#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x){{$}}
+
+bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);
+// CHECK-FIXES: {{^}}bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);{{$}}
+
+bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);
+// CHECK-FIXES: {{^}}bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);{{$}}
+
+class FooClass {
+ public:
+ FooClass() : JustBool(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}FooClass() : JustBool(false) {}{{$}}
+ FooClass(int) : JustBool{0} {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}FooClass(int) : JustBool{false} {}{{$}}
+ private:
+ bool JustBool;
+ bool BoolWithBraces{0};
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool BoolWithBraces{false};{{$}}
+ bool BoolFromInt = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool BoolFromInt = false;{{$}}
+ bool SimpleBool = true; // OK
+};
+
+template<typename type>
+void templateFunction(type) {
+ type TemplateType = 0;
+ // CHECK-FIXES: {{^ *}}type TemplateType = 0;{{$}}
+}
+
+template<int c>
+void valueDependentTemplateFunction() {
+ bool Boolean = c;
+ // CHECK-FIXES: {{^ *}}bool Boolean = c;{{$}}
+}
+
+template<typename type>
+void anotherTemplateFunction(type) {
+ bool JustBool = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool JustBool = false;{{$}}
+}
+
+int main() {
+ boolFunction(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}boolFunction(true);{{$}}
+
+ boolFunction(false);
+
+ templateFunction(0);
+
+ templateFunction(false);
+
+ valueDependentTemplateFunction<1>();
+
+ anotherTemplateFunction(1);
+
+ IntToTrue = 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}IntToTrue = true;{{$}}
+}
+
+static int Value = 1;
+
+bool Function1() {
+ bool Result = Value == 1 ? 1 : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: converting integer literal to bool
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool Result = Value == 1 ? true : false;{{$}}
+ return Result;
+}
+
+bool Function2() {
+ return Value == 1 ? 1 : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+ // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}return Value == 1 ? true : false;{{$}}
+}
+
+void foo() {
+ bool Result;
+ Result = Value == 1 ? true : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? true : false;{{$}}
+ Result = Value == 1 ? false : bool(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+ Result = Value == 1 ? (bool)0 : false;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-bool-literals %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: modernize-use-bool-literals.IgnoreMacros, \
+// RUN: value: 0}]}" \
+// RUN: -- -std=c++11
+
+bool IntToTrue = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: converting integer literal to bool, use bool literal instead [modernize-use-bool-literals]
+// CHECK-FIXES: {{^}}bool IntToTrue = true;{{$}}
+
+bool IntToFalse(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool IntToFalse(false);{{$}}
+
+bool LongLongToTrue{0x1LL};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool LongLongToTrue{true};{{$}}
+
+bool ExplicitCStyleIntToFalse = (bool)0;
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitCStyleIntToFalse = false;{{$}}
+
+bool ExplicitFunctionalIntToFalse = bool(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitFunctionalIntToFalse = false;{{$}}
+
+bool ExplicitStaticIntToFalse = static_cast<bool>(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitStaticIntToFalse = false;{{$}}
+
+#define TRUE_MACRO 1
+// CHECK-FIXES: {{^}}#define TRUE_MACRO 1{{$}}
+
+bool MacroIntToTrue = TRUE_MACRO;
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool MacroIntToTrue = TRUE_MACRO;{{$}}
+
+#define FALSE_MACRO bool(0)
+// CHECK-FIXES: {{^}}#define FALSE_MACRO bool(0){{$}}
+
+bool TrueBool = true; // OK
+
+bool FalseBool = bool(FALSE_MACRO);
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool FalseBool = bool(FALSE_MACRO);{{$}}
+
+void boolFunction(bool bar) {
+
+}
+
+char Character = 0; // OK
+
+unsigned long long LongInteger = 1; // OK
+
+#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x)
+// CHECK-FIXES: {{^}}#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x){{$}}
+
+bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);{{$}}
+
+bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);
+// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);{{$}}
+
+class FooClass {
+ public:
+ FooClass() : JustBool(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}FooClass() : JustBool(false) {}{{$}}
+ FooClass(int) : JustBool{0} {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}FooClass(int) : JustBool{false} {}{{$}}
+ private:
+ bool JustBool;
+ bool BoolWithBraces{0};
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool BoolWithBraces{false};{{$}}
+ bool BoolFromInt = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool BoolFromInt = false;{{$}}
+ bool SimpleBool = true; // OK
+};
+
+template<typename type>
+void templateFunction(type) {
+ type TemplateType = 0;
+ // CHECK-FIXES: {{^ *}}type TemplateType = 0;{{$}}
+}
+
+template<int c>
+void valueDependentTemplateFunction() {
+ bool Boolean = c;
+ // CHECK-FIXES: {{^ *}}bool Boolean = c;{{$}}
+}
+
+template<typename type>
+void anotherTemplateFunction(type) {
+ bool JustBool = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool JustBool = false;{{$}}
+}
+
+int main() {
+ boolFunction(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}boolFunction(true);{{$}}
+
+ boolFunction(false);
+
+ templateFunction(0);
+
+ templateFunction(false);
+
+ valueDependentTemplateFunction<1>();
+
+ anotherTemplateFunction(1);
+
+ IntToTrue = 1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}IntToTrue = true;{{$}}
+}
+
+static int Value = 1;
+
+bool Function1() {
+ bool Result = Value == 1 ? 1 : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: converting integer literal to bool
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}bool Result = Value == 1 ? true : false;{{$}}
+ return Result;
+}
+
+bool Function2() {
+ return Value == 1 ? 1 : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+ // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}return Value == 1 ? true : false;{{$}}
+}
+
+void foo() {
+ bool Result;
+ Result = Value == 1 ? true : 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? true : false;{{$}}
+ Result = Value == 1 ? false : bool(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+ Result = Value == 1 ? (bool)0 : false;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+ // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-default-member-init.UseAssignment, value: 1}]}" -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueChar {
+ PositiveValueChar() : c0(), c1()/*, c2(), c3()*/ {}
+ // CHECK-FIXES: PositiveValueChar() {}
+ const char c0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use default member initializer for 'c0' [modernize-use-default-member-init]
+ // CHECK-FIXES: const char c0 = '\0';
+ wchar_t c1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use default member initializer for 'c1'
+ // CHECK-FIXES: wchar_t c1 = L'\0';
+ // FIXME: char16_t c2;
+ // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c2'
+ // C HECK-FIXES: char16_t c2 = u'\0';
+ // FIXME: char32_t c3;
+ // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c3'
+ // C HECK-FIXES: char32_t c3 = U'\0';
+};
+
+struct PositiveChar {
+ PositiveChar() : d('a') {}
+ // CHECK-FIXES: PositiveChar() {}
+ char d;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'd'
+ // CHECK-FIXES: char d = 'a';
+};
+
+struct PositiveValueInt {
+ PositiveValueInt() : i() {}
+ // CHECK-FIXES: PositiveValueInt() {}
+ const int i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i'
+ // CHECK-FIXES: const int i = 0;
+};
+
+struct PositiveInt {
+ PositiveInt() : j(1) {}
+ // CHECK-FIXES: PositiveInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j = 1;
+};
+
+struct PositiveUnaryMinusInt {
+ PositiveUnaryMinusInt() : j(-1) {}
+ // CHECK-FIXES: PositiveUnaryMinusInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j = -1;
+};
+
+struct PositiveUnaryPlusInt {
+ PositiveUnaryPlusInt() : j(+1) {}
+ // CHECK-FIXES: PositiveUnaryPlusInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j = +1;
+};
+
+struct PositiveValueComplexInt {
+ PositiveValueComplexInt() : i() {}
+ // CHECK-FIXES: PositiveValueComplexInt() {}
+ _Complex int i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use default member initializer for 'i'
+ // CHECK-FIXES: _Complex int i = 0;
+};
+
+struct PositiveValueFloat {
+ PositiveValueFloat() : f() {}
+ // CHECK-FIXES: PositiveValueFloat() {}
+ float f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'f'
+ // CHECK-FIXES: float f = 0.0f;
+};
+
+struct PositiveValueDouble {
+ PositiveValueDouble() : d() {}
+ // CHECK-FIXES: PositiveValueDouble() {}
+ double d;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd'
+ // CHECK-FIXES: double d = 0.0;
+};
+
+struct PositiveDouble {
+ PositiveDouble() : f(2.5463e43) {}
+ // CHECK-FIXES: PositiveDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f = 2.5463e43;
+};
+
+struct PositiveValueComplexFloat {
+ PositiveValueComplexFloat() : f() {}
+ // CHECK-FIXES: PositiveValueComplexFloat() {}
+ _Complex float f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use default member initializer for 'f'
+ // CHECK-FIXES: _Complex float f = 0.0f;
+};
+
+struct PositiveValueComplexDouble {
+ PositiveValueComplexDouble() : f() {}
+ // CHECK-FIXES: PositiveValueComplexDouble() {}
+ _Complex double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use default member initializer for 'f'
+ // CHECK-FIXES: _Complex double f = 0.0;
+};
+
+struct PositiveUnaryMinusDouble {
+ PositiveUnaryMinusDouble() : f(-2.5463e43) {}
+ // CHECK-FIXES: PositiveUnaryMinusDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f = -2.5463e43;
+};
+
+struct PositiveUnaryPlusDouble {
+ PositiveUnaryPlusDouble() : f(+2.5463e43) {}
+ // CHECK-FIXES: PositiveUnaryPlusDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f = +2.5463e43;
+};
+
+struct PositiveValueBool {
+ PositiveValueBool() : b() {}
+ // CHECK-FIXES: PositiveValueBool() {}
+ bool b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b'
+ // CHECK-FIXES: bool b = false;
+};
+
+struct PositiveBool {
+ PositiveBool() : a(true) {}
+ // CHECK-FIXES: PositiveBool() {}
+ bool a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a'
+ // CHECK-FIXES: bool a = true;
+};
+
+struct PositiveValuePointer {
+ PositiveValuePointer() : p() {}
+ // CHECK-FIXES: PositiveValuePointer() {}
+ int *p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p'
+ // CHECK-FIXES: int *p = nullptr;
+};
+
+struct PositiveNullPointer {
+ PositiveNullPointer() : q(nullptr) {}
+ // CHECK-FIXES: PositiveNullPointer() {}
+ int *q;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q'
+ // CHECK-FIXES: int *q = nullptr;
+};
+
+enum Enum { Foo };
+struct PositiveEnum {
+ PositiveEnum() : e(Foo) {}
+ // CHECK-FIXES: PositiveEnum() {}
+ Enum e;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e'
+ // CHECK-FIXES: Enum e = Foo;
+};
+
+struct PositiveString {
+ PositiveString() : s("foo") {}
+ // CHECK-FIXES: PositiveString() {}
+ const char *s;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use default member initializer for 's'
+ // CHECK-FIXES: const char *s = "foo";
+};
+
+template <typename T>
+struct NegativeTemplate {
+ NegativeTemplate() : t() {}
+ T t;
+};
+
+NegativeTemplate<int> nti;
+NegativeTemplate<double> ntd;
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-default-member-init.IgnoreMacros, value: 0}]}" \
+// RUN: -- -std=c++11
+
+#define MACRO() \
+ struct S { \
+ void *P; \
+ S() : P(nullptr) {} \
+ };
+
+MACRO();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use default member initializer for 'P'
+
+struct S2 {
+ void *P;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'P'
+ S2() : P(nullptr) {}
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueChar {
+ PositiveValueChar() : c0(), c1()/*, c2(), c3()*/ {}
+ // CHECK-FIXES: PositiveValueChar() {}
+ const char c0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use default member initializer for 'c0' [modernize-use-default-member-init]
+ // CHECK-FIXES: const char c0{};
+ wchar_t c1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use default member initializer for 'c1'
+ // CHECK-FIXES: wchar_t c1{};
+ // FIXME: char16_t c2;
+ // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c2'
+ // C HECK-FIXES: char16_t c2{};
+ // FIXME: char32_t c3;
+ // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c3'
+ // C HECK-FIXES: char32_t c3{};
+};
+
+struct PositiveChar {
+ PositiveChar() : d('a') {}
+ // CHECK-FIXES: PositiveChar() {}
+ char d;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'd'
+ // CHECK-FIXES: char d{'a'};
+};
+
+struct PositiveValueInt {
+ PositiveValueInt() : i() {}
+ // CHECK-FIXES: PositiveValueInt() {}
+ const int i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i'
+ // CHECK-FIXES: const int i{};
+};
+
+struct PositiveInt {
+ PositiveInt() : j(1) {}
+ // CHECK-FIXES: PositiveInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j{1};
+};
+
+struct PositiveUnaryMinusInt {
+ PositiveUnaryMinusInt() : j(-1) {}
+ // CHECK-FIXES: PositiveUnaryMinusInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j{-1};
+};
+
+struct PositiveUnaryPlusInt {
+ PositiveUnaryPlusInt() : j(+1) {}
+ // CHECK-FIXES: PositiveUnaryPlusInt() {}
+ int j;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+ // CHECK-FIXES: int j{+1};
+};
+
+struct PositiveValueComplexInt {
+ PositiveValueComplexInt() : i() {}
+ // CHECK-FIXES: PositiveValueComplexInt() {}
+ _Complex int i;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use default member initializer for 'i'
+ // CHECK-FIXES: _Complex int i{};
+};
+
+struct PositiveValueFloat {
+ PositiveValueFloat() : f() {}
+ // CHECK-FIXES: PositiveValueFloat() {}
+ float f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'f'
+ // CHECK-FIXES: float f{};
+};
+
+struct PositiveValueDouble {
+ PositiveValueDouble() : d() {}
+ // CHECK-FIXES: PositiveValueDouble() {}
+ double d;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd'
+ // CHECK-FIXES: double d{};
+};
+
+struct PositiveDouble {
+ PositiveDouble() : f(2.5463e43) {}
+ // CHECK-FIXES: PositiveDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f{2.5463e43};
+};
+
+struct PositiveValueComplexFloat {
+ PositiveValueComplexFloat() : f() {}
+ // CHECK-FIXES: PositiveValueComplexFloat() {}
+ _Complex float f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use default member initializer for 'f'
+ // CHECK-FIXES: _Complex float f{};
+};
+
+struct PositiveValueComplexDouble {
+ PositiveValueComplexDouble() : f() {}
+ // CHECK-FIXES: PositiveValueComplexDouble() {}
+ _Complex double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use default member initializer for 'f'
+ // CHECK-FIXES: _Complex double f{};
+};
+
+struct PositiveUnaryMinusDouble {
+ PositiveUnaryMinusDouble() : f(-2.5463e43) {}
+ // CHECK-FIXES: PositiveUnaryMinusDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f{-2.5463e43};
+};
+
+struct PositiveUnaryPlusDouble {
+ PositiveUnaryPlusDouble() : f(+2.5463e43) {}
+ // CHECK-FIXES: PositiveUnaryPlusDouble() {}
+ double f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+ // CHECK-FIXES: double f{+2.5463e43};
+};
+
+struct PositiveValueBool {
+ PositiveValueBool() : b() {}
+ // CHECK-FIXES: PositiveValueBool() {}
+ bool b;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b'
+ // CHECK-FIXES: bool b{};
+};
+
+struct PositiveBool {
+ PositiveBool() : a(true) {}
+ // CHECK-FIXES: PositiveBool() {}
+ bool a;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a'
+ // CHECK-FIXES: bool a{true};
+};
+
+struct PositiveValuePointer {
+ PositiveValuePointer() : p() {}
+ // CHECK-FIXES: PositiveValuePointer() {}
+ int *p;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p'
+ // CHECK-FIXES: int *p{};
+};
+
+struct PositiveNullPointer {
+ PositiveNullPointer() : q(nullptr) {}
+ // CHECK-FIXES: PositiveNullPointer() {}
+ int *q;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q'
+ // CHECK-FIXES: int *q{nullptr};
+};
+
+enum Enum { Foo, Bar };
+struct PositiveEnum {
+ PositiveEnum() : e(Foo) {}
+ // CHECK-FIXES: PositiveEnum() {}
+ Enum e;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e'
+ // CHECK-FIXES: Enum e{Foo};
+};
+
+struct PositiveString {
+ PositiveString() : s("foo") {}
+ // CHECK-FIXES: PositiveString() {}
+ const char *s;
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use default member initializer for 's'
+ // CHECK-FIXES: const char *s{"foo"};
+};
+
+template <typename T>
+struct NegativeTemplate {
+ NegativeTemplate() : t() {}
+ T t;
+};
+
+NegativeTemplate<int> nti;
+NegativeTemplate<double> ntd;
+
+struct NegativeDefaultMember {
+ NegativeDefaultMember() {}
+ int i = 2;
+};
+
+struct NegativeClass : S {
+ NegativeClass() : s() {}
+ S s;
+};
+
+struct NegativeBase : S {
+ NegativeBase() : S() {}
+};
+
+struct NegativeDefaultOtherMember{
+ NegativeDefaultOtherMember() : i(3) {}
+ int i = 4;
+};
+
+struct NegativeUnion {
+ NegativeUnion() : d(5.0) {}
+ union {
+ int i;
+ double d;
+ };
+};
+
+struct NegativeBitField
+{
+ NegativeBitField() : i(6) {}
+ int i : 5;
+};
+
+struct NegativeNotDefaultInt
+{
+ NegativeNotDefaultInt(int) : i(7) {}
+ int i;
+};
+
+struct NegativeDefaultArg
+{
+ NegativeDefaultArg(int i = 4) : i(i) {}
+ int i;
+};
+
+struct ExistingChar {
+ ExistingChar(short) : e1(), e2(), e3(), e4() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+ // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:37: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingChar(short) : e4() {}
+ ExistingChar(int) : e1(0), e2(0), e3(0), e4(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:37: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingChar(int) : e4(0) {}
+ ExistingChar(long) : e1('\0'), e2('\0'), e3('\0'), e4('\0') {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:44: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingChar(long) : e4('\0') {}
+ ExistingChar(char) : e1('a'), e2('a'), e3('a'), e4('a') {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: member initializer for 'e4' is redundant
+ // CHECK-FIXES: ExistingChar(char) : e1('a'), e2('a'), e3('a') {}
+ char e1{};
+ char e2 = 0;
+ char e3 = '\0';
+ char e4 = 'a';
+};
+
+struct ExistingInt {
+ ExistingInt(short) : e1(), e2(), e3(), e4(), e5(), e6() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+ // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingInt(short) : e3(), e4(), e5(), e6() {}
+ ExistingInt(int) : e1(0), e2(0), e3(0), e4(0), e5(0), e6(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingInt(int) : e3(0), e4(0), e5(0), e6(0) {}
+ ExistingInt(long) : e1(5), e2(5), e3(5), e4(5), e5(5), e6(5) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: member initializer for 'e3' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: member initializer for 'e4' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: member initializer for 'e6' is redundant
+ // CHECK-FIXES: ExistingInt(long) : e1(5), e2(5), e5(5) {}
+ ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5), e5(-5), e6(-5) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:55: warning: member initializer for 'e5' is redundant
+ // CHECK-FIXES: ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5), e6(-5) {}
+ int e1{};
+ int e2 = 0;
+ int e3 = {5};
+ int e4 = 5;
+ int e5 = -5;
+ int e6 = +5;
+};
+
+struct ExistingDouble {
+ ExistingDouble(short) : e1(), e2(), e3(), e4(), e5() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingDouble(short) : e3(), e4(), e5() {}
+ ExistingDouble(int) : e1(0.0), e2(0.0), e3(0.0), e4(0.0), e5(0.0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingDouble(int) : e3(0.0), e4(0.0), e5(0.0) {}
+ ExistingDouble(long) : e1(5.0), e2(5.0), e3(5.0), e4(5.0), e5(5.0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:62: warning: member initializer for 'e5' is redundant
+ // CHECK-FIXES: ExistingDouble(long) : e1(5.0), e2(5.0), e4(5.0) {}
+ ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0), e4(-5.0), e5(-5.0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: member initializer for 'e4' is redundant
+ // CHECK-FIXES: ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0), e5(-5.0) {}
+ double e1{};
+ double e2 = 0.0;
+ double e3 = 5.0;
+ double e4 = -5.0;
+ double e5 = +5.0;
+};
+
+struct ExistingBool {
+ ExistingBool(short) : e1(), e2(), e3() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingBool(short) : e3() {}
+ ExistingBool(int) : e1(false), e2(false), e3(false) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingBool(int) : e3(false) {}
+ ExistingBool(long) : e1(true), e2(true), e3(true) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingBool(long) : e1(true), e2(true) {}
+ bool e1{};
+ bool e2 = false;
+ bool e3 = true;
+};
+
+struct ExistingEnum {
+ ExistingEnum(short) : e1(Foo), e2(Foo) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+ // CHECK-FIXES: ExistingEnum(short) : e2(Foo) {}
+ ExistingEnum(int) : e1(Bar), e2(Bar) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingEnum(int) : e1(Bar) {}
+ Enum e1 = Foo;
+ Enum e2{Bar};
+};
+
+struct ExistingPointer {
+ ExistingPointer(short) : e1(), e2(), e3(), e4() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingPointer(short) : e4() {}
+ ExistingPointer(int) : e1(0), e2(0), e3(0), e4(&e1) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingPointer(int) : e4(&e1) {}
+ ExistingPointer(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(&e2) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: member initializer for 'e2' is redundant
+ // CHECK-MESSAGES: :[[@LINE-3]]:53: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingPointer(long) : e4(&e2) {}
+ int *e1{};
+ int *e2 = 0;
+ int *e3 = nullptr;
+ int **e4 = &e1;
+};
+
+struct ExistingString {
+ ExistingString(short) : e1(), e2(), e3(), e4() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingString(short) : e3(), e4() {}
+ ExistingString(int) : e1(0), e2(0), e3(0), e4(0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingString(int) : e3(0), e4(0) {}
+ ExistingString(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(nullptr) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: member initializer for 'e2' is redundant
+ // CHECK-FIXES: ExistingString(long) : e3(nullptr), e4(nullptr) {}
+ ExistingString(char) : e1("foo"), e2("foo"), e3("foo"), e4("foo") {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: member initializer for 'e3' is redundant
+ // CHECK-FIXES: ExistingString(char) : e1("foo"), e2("foo"), e4("foo") {}
+ const char *e1{};
+ const char *e2 = nullptr;
+ const char *e3 = "foo";
+ const char *e4 = "bar";
+};
+
+template <typename T>
+struct NegativeTemplateExisting {
+ NegativeTemplateExisting(int) : t(0) {}
+ T t{};
+};
+
+NegativeTemplateExisting<int> ntei(0);
+NegativeTemplateExisting<double> nted(0);
+
+// This resulted in a warning by default.
+#define MACRO() \
+ struct MacroS { \
+ void *P; \
+ MacroS() : P(nullptr) {} \
+ };
+
+MACRO();
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-emplace %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: modernize-use-emplace.ContainersWithPushBack, \
+// RUN: value: '::std::vector; ::std::list; ::std::deque; llvm::LikeASmallVector'}, \
+// RUN: {key: modernize-use-emplace.TupleTypes, \
+// RUN: value: '::std::pair; std::tuple; ::test::Single'}, \
+// RUN: {key: modernize-use-emplace.TupleMakeFunctions, \
+// RUN: value: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}] \
+// RUN: }" -- -std=c++11
+
+namespace std {
+template <typename>
+class initializer_list
+{
+public:
+ initializer_list() noexcept {}
+};
+
+template <typename T>
+class vector {
+public:
+ vector() = default;
+ vector(initializer_list<T>) {}
+
+ void push_back(const T &) {}
+ void push_back(T &&) {}
+
+ template <typename... Args>
+ void emplace_back(Args &&... args){};
+ ~vector();
+};
+template <typename T>
+class list {
+public:
+ void push_back(const T &) {}
+ void push_back(T &&) {}
+
+ template <typename... Args>
+ void emplace_back(Args &&... args){};
+ ~list();
+};
+
+template <typename T>
+class deque {
+public:
+ void push_back(const T &) {}
+ void push_back(T &&) {}
+
+ template <typename... Args>
+ void emplace_back(Args &&... args){};
+ ~deque();
+};
+
+template <typename T> struct remove_reference { using type = T; };
+template <typename T> struct remove_reference<T &> { using type = T; };
+template <typename T> struct remove_reference<T &&> { using type = T; };
+
+template <typename T1, typename T2> class pair {
+public:
+ pair() = default;
+ pair(const pair &) = default;
+ pair(pair &&) = default;
+
+ pair(const T1 &, const T2 &) {}
+ pair(T1 &&, T2 &&) {}
+
+ template <typename U1, typename U2> pair(const pair<U1, U2> &){};
+ template <typename U1, typename U2> pair(pair<U1, U2> &&){};
+};
+
+template <typename T1, typename T2>
+pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
+make_pair(T1 &&, T2 &&) {
+ return {};
+};
+
+template <typename... Ts> class tuple {
+public:
+ tuple() = default;
+ tuple(const tuple &) = default;
+ tuple(tuple &&) = default;
+
+ tuple(const Ts &...) {}
+ tuple(Ts &&...) {}
+
+ template <typename... Us> tuple(const tuple<Us...> &){};
+ template <typename... Us> tuple(tuple<Us...> &&) {}
+
+ template <typename U1, typename U2> tuple(const pair<U1, U2> &) {
+ static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
+ };
+ template <typename U1, typename U2> tuple(pair<U1, U2> &&) {
+ static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
+ };
+};
+
+template <typename... Ts>
+tuple<typename remove_reference<Ts>::type...> make_tuple(Ts &&...) {
+ return {};
+}
+
+template <typename T>
+class unique_ptr {
+public:
+ explicit unique_ptr(T *) {}
+ ~unique_ptr();
+};
+} // namespace std
+
+namespace llvm {
+template <typename T>
+class LikeASmallVector {
+public:
+ void push_back(const T &) {}
+ void push_back(T &&) {}
+
+ template <typename... Args>
+ void emplace_back(Args &&... args){};
+};
+
+} // llvm
+
+void testInts() {
+ std::vector<int> v;
+ v.push_back(42);
+ v.push_back(int(42));
+ v.push_back(int{42});
+ v.push_back(42.0);
+ int z;
+ v.push_back(z);
+}
+
+struct Something {
+ Something(int a, int b = 41) {}
+ Something() {}
+ void push_back(Something);
+ int getInt() { return 42; }
+};
+
+struct Convertable {
+ operator Something() { return Something{}; }
+};
+
+struct Zoz {
+ Zoz(Something, int = 42) {}
+};
+
+Zoz getZoz(Something s) { return Zoz(s); }
+
+void test_Something() {
+ std::vector<Something> v;
+
+ v.push_back(Something(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back instead of push_back [modernize-use-emplace]
+ // CHECK-FIXES: v.emplace_back(1, 2);
+
+ v.push_back(Something{1, 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2);
+
+ v.push_back(Something());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back();
+
+ v.push_back(Something{});
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back();
+
+ Something Different;
+ v.push_back(Something(Different.getInt(), 42));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(Different.getInt(), 42);
+
+ v.push_back(Different.getInt());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(Different.getInt());
+
+ v.push_back(42);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(42);
+
+ Something temporary(42, 42);
+ temporary.push_back(temporary);
+ v.push_back(temporary);
+
+ v.push_back(Convertable());
+ v.push_back(Convertable{});
+ Convertable s;
+ v.push_back(s);
+}
+
+template <typename ElemType>
+void dependOnElem() {
+ std::vector<ElemType> v;
+ v.push_back(ElemType(42));
+}
+
+template <typename ContainerType>
+void dependOnContainer() {
+ ContainerType v;
+ v.push_back(Something(42));
+}
+
+void callDependent() {
+ dependOnElem<Something>();
+ dependOnContainer<std::vector<Something>>();
+}
+
+void test2() {
+ std::vector<Zoz> v;
+ v.push_back(Zoz(Something(21, 37)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(Something(21, 37));
+
+ v.push_back(Zoz(Something(21, 37), 42));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(Something(21, 37), 42);
+
+ v.push_back(getZoz(Something(1, 2)));
+}
+
+struct GetPair {
+ std::pair<int, long> getPair();
+};
+void testPair() {
+ std::vector<std::pair<int, int>> v;
+ v.push_back(std::pair<int, int>(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2);
+
+ GetPair g;
+ v.push_back(g.getPair());
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(g.getPair());
+
+ std::vector<std::pair<Something, Zoz>> v2;
+ v2.push_back(std::pair<Something, Zoz>(Something(42, 42), Zoz(Something(21, 37))));
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
+ // CHECK-FIXES: v2.emplace_back(Something(42, 42), Zoz(Something(21, 37)));
+}
+
+void testTuple() {
+ std::vector<std::tuple<bool, char, int>> v;
+ v.push_back(std::tuple<bool, char, int>(false, 'x', 1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(false, 'x', 1);
+
+ v.push_back(std::tuple<bool, char, int>{false, 'y', 2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(false, 'y', 2);
+
+ v.push_back({true, 'z', 3});
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(true, 'z', 3);
+
+ std::vector<std::tuple<int, bool>> x;
+ x.push_back(std::make_pair(1, false));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: x.emplace_back(1, false);
+
+ x.push_back(std::make_pair(2LL, 1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: x.emplace_back(2LL, 1);
+}
+
+struct Base {
+ Base(int, int *, int = 42);
+};
+
+struct Derived : Base {
+ Derived(int *, Something) : Base(42, nullptr) {}
+};
+
+void testDerived() {
+ std::vector<Base> v;
+ v.push_back(Derived(nullptr, Something{}));
+}
+
+void testNewExpr() {
+ std::vector<Derived> v;
+ v.push_back(Derived(new int, Something{}));
+}
+
+void testSpaces() {
+ std::vector<Something> v;
+
+ // clang-format off
+
+ v.push_back(Something(1, //arg1
+ 2 // arg2
+ ) // Something
+ );
+ // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, //arg1
+ // CHECK-FIXES: 2 // arg2
+ // CHECK-FIXES: // Something
+ // CHECK-FIXES: );
+
+ v.push_back( Something (1, 2) );
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2 );
+
+ v.push_back( Something {1, 2} );
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2 );
+
+ v.push_back( Something {} );
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back( );
+
+ v.push_back(
+ Something(1, 2) );
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2 );
+
+ std::vector<Base> v2;
+ v2.push_back(
+ Base(42, nullptr));
+ // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: use emplace_back
+ // CHECK-FIXES: v2.emplace_back(42, nullptr);
+
+ // clang-format on
+}
+
+void testPointers() {
+ std::vector<int *> v;
+ v.push_back(new int(5));
+
+ std::vector<std::unique_ptr<int>> v2;
+ v2.push_back(std::unique_ptr<int>(new int(42)));
+ // This call can't be replaced with emplace_back.
+ // If emplacement will fail (not enough memory to add to vector)
+ // we will have leak of int because unique_ptr won't be constructed
+ // (and destructed) as in push_back case.
+
+ auto *ptr = new int;
+ v2.push_back(std::unique_ptr<int>(ptr));
+ // Same here
+}
+
+void testMakePair() {
+ std::vector<std::pair<int, int>> v;
+ v.push_back(std::make_pair(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, 2);
+
+ v.push_back(std::make_pair(42LL, 13));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(42LL, 13);
+
+ v.push_back(std::make_pair<char, char>(0, 3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(std::make_pair<char, char>(0, 3));
+ //
+ // Even though the call above could be turned into v.emplace_back(0, 3),
+ // we don't eliminate the make_pair call here, because of the explicit
+ // template parameters provided. make_pair's arguments can be convertible
+ // to its explicitly provided template parameter, but not to the pair's
+ // element type. The examples below illustrate the problem.
+ struct D {
+ D(...) {}
+ operator char() const { return 0; }
+ };
+ v.push_back(std::make_pair<D, int>(Something(), 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(std::make_pair<D, int>(Something(), 2));
+
+ struct X {
+ X(std::pair<int, int>) {}
+ };
+ std::vector<X> x;
+ x.push_back(std::make_pair(1, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: x.emplace_back(std::make_pair(1, 2));
+ // make_pair cannot be removed here, as X is not constructible with two ints.
+
+ struct Y {
+ Y(std::pair<int, int>&&) {}
+ };
+ std::vector<Y> y;
+ y.push_back(std::make_pair(2, 3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: y.emplace_back(std::make_pair(2, 3));
+ // make_pair cannot be removed here, as Y is not constructible with two ints.
+}
+
+void testMakeTuple() {
+ std::vector<std::tuple<int, bool, char>> v;
+ v.push_back(std::make_tuple(1, true, 'v'));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1, true, 'v');
+
+ v.push_back(std::make_tuple(2ULL, 1, 0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(2ULL, 1, 0);
+
+ v.push_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
+ // make_tuple is not removed when there are explicit template
+ // arguments provided.
+}
+
+namespace test {
+template <typename T> struct Single {
+ Single() = default;
+ Single(const Single &) = default;
+ Single(Single &&) = default;
+
+ Single(const T &) {}
+ Single(T &&) {}
+
+ template <typename U> Single(const Single<U> &) {}
+ template <typename U> Single(Single<U> &&) {}
+
+ template <typename U> Single(const std::tuple<U> &) {}
+ template <typename U> Single(std::tuple<U> &&) {}
+};
+
+template <typename T>
+Single<typename std::remove_reference<T>::type> MakeSingle(T &&) {
+ return {};
+}
+} // namespace test
+
+void testOtherTuples() {
+ std::vector<test::Single<int>> v;
+ v.push_back(test::Single<int>(1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(1);
+
+ v.push_back({2});
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(2);
+
+ v.push_back(test::MakeSingle(3));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(3);
+
+ v.push_back(test::MakeSingle<long long>(4));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(test::MakeSingle<long long>(4));
+ // We don't remove make functions with explicit template parameters.
+
+ v.push_back(test::MakeSingle(5LL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(5LL);
+
+ v.push_back(std::make_tuple(6));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(6);
+
+ v.push_back(std::make_tuple(7LL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(7LL);
+}
+
+void testOtherContainers() {
+ std::list<Something> l;
+ l.push_back(Something(42, 41));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: l.emplace_back(42, 41);
+
+ std::deque<Something> d;
+ d.push_back(Something(42));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: d.emplace_back(42);
+
+ llvm::LikeASmallVector<Something> ls;
+ ls.push_back(Something(42));
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
+ // CHECK-FIXES: ls.emplace_back(42);
+}
+
+class IntWrapper {
+public:
+ IntWrapper(int x) : value(x) {}
+ IntWrapper operator+(const IntWrapper other) const {
+ return IntWrapper(value + other.value);
+ }
+
+private:
+ int value;
+};
+
+void testMultipleOpsInPushBack() {
+ std::vector<IntWrapper> v;
+ v.push_back(IntWrapper(42) + IntWrapper(27));
+}
+
+// Macro tests.
+#define PUSH_BACK_WHOLE(c, x) c.push_back(x)
+#define PUSH_BACK_NAME push_back
+#define PUSH_BACK_ARG(x) (x)
+#define SOME_OBJ Something(10)
+#define MILLION 3
+#define SOME_WEIRD_PUSH(v) v.push_back(Something(
+#define OPEN (
+#define CLOSE )
+void macroTest() {
+ std::vector<Something> v;
+ Something s;
+
+ PUSH_BACK_WHOLE(v, Something(5, 6));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use emplace_back
+
+ v.PUSH_BACK_NAME(Something(5));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+ v.push_back PUSH_BACK_ARG(Something(5, 6));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+ v.push_back(SOME_OBJ);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+ v.push_back(Something(MILLION));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(MILLION);
+
+ // clang-format off
+ v.push_back( Something OPEN 3 CLOSE );
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // clang-format on
+ PUSH_BACK_WHOLE(s, Something(1));
+}
+
+struct A {
+ int value1, value2;
+};
+
+struct B {
+ B(A) {}
+};
+
+struct C {
+ int value1, value2, value3;
+};
+
+void testAggregation() {
+ // This should not be noticed or fixed; after the correction, the code won't
+ // compile.
+
+ std::vector<A> v;
+ v.push_back(A({1, 2}));
+
+ std::vector<B> vb;
+ vb.push_back(B({10, 42}));
+}
+
+struct Bitfield {
+ unsigned bitfield : 1;
+ unsigned notBitfield;
+};
+
+void testBitfields() {
+ std::vector<Something> v;
+ Bitfield b;
+ v.push_back(Something(42, b.bitfield));
+ v.push_back(Something(b.bitfield));
+
+ v.push_back(Something(42, b.notBitfield));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(42, b.notBitfield);
+ int var;
+ v.push_back(Something(42, var));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(42, var);
+}
+
+class PrivateCtor {
+ PrivateCtor(int z);
+
+public:
+ void doStuff() {
+ std::vector<PrivateCtor> v;
+ // This should not change it because emplace back doesn't have permission.
+ // Check currently doesn't support friend declarations because pretty much
+ // nobody would want to be friend with std::vector :(.
+ v.push_back(PrivateCtor(42));
+ }
+};
+
+struct WithDtor {
+ WithDtor(int) {}
+ ~WithDtor();
+};
+
+void testWithDtor() {
+ std::vector<WithDtor> v;
+
+ v.push_back(WithDtor(42));
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+ // CHECK-FIXES: v.emplace_back(42);
+}
+
+void testInitializerList() {
+ std::vector<std::vector<int>> v;
+ v.push_back(std::vector<int>({1}));
+ // Test against the bug reported in PR32896.
+
+ v.push_back({{2}});
+
+ using PairIntVector = std::pair<int, std::vector<int>>;
+ std::vector<PairIntVector> x;
+ x.push_back(PairIntVector(3, {4}));
+ x.push_back({5, {6}});
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions
+
+// Out of line definition.
+struct OL {
+ OL(const OL &);
+ OL &operator=(const OL &);
+ int Field;
+};
+OL::OL(const OL &Other) : Field(Other.Field) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial copy constructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::OL(const OL &Other) = default;
+OL &OL::operator=(const OL &Other) {
+ Field = Other.Field;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: use '= default' to define a trivial copy-assignment operator [modernize-use-equals-default]
+// CHECK-FIXES: OL &OL::operator=(const OL &Other) = default;
+
+// Inline.
+struct IL {
+ IL(const IL &Other) : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: IL(const IL &Other) = default;
+ IL &operator=(const IL &Other) {
+ Field = Other.Field;
+ return *this;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use '= default'
+ // CHECK-FIXES: IL &operator=(const IL &Other) = default;
+ int Field;
+};
+
+// Wrong type.
+struct WT {
+ WT(const IL &Other) {}
+ WT &operator=(const IL &);
+};
+WT &WT::operator=(const IL &Other) { return *this; }
+
+// Qualifiers.
+struct Qual {
+ Qual(const Qual &Other) : Field(Other.Field), Volatile(Other.Volatile),
+ Mutable(Other.Mutable), Reference(Other.Reference),
+ Const(Other.Const) {}
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+ // CHECK-FIXES: Qual(const Qual &Other)
+ // CHECK-FIXES: = default;
+
+ int Field;
+ volatile char Volatile;
+ mutable bool Mutable;
+ const OL &Reference; // This makes this class non-assignable.
+ const IL Const; // This also makes this class non-assignable.
+ static int Static;
+};
+
+// Wrong init arguments.
+struct WI {
+ WI(const WI &Other) : Field1(Other.Field1), Field2(Other.Field1) {}
+ WI &operator=(const WI &);
+ int Field1, Field2;
+};
+WI &WI::operator=(const WI &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field1;
+ return *this;
+}
+
+// Missing field.
+struct MF {
+ MF(const MF &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ MF &operator=(const MF &);
+ int Field1, Field2, Field3;
+};
+MF &MF::operator=(const MF &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+
+struct Comments {
+ Comments(const Comments &Other)
+ /* don't delete */ : /* this comment */ Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ // CHECK-FIXES: /* don't delete */ = default;
+ int Field;
+};
+
+struct MoreComments {
+ MoreComments(const MoreComments &Other) /* this comment is OK */
+ : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ // CHECK-FIXES: MoreComments(const MoreComments &Other) /* this comment is OK */
+ // CHECK-FIXES-NEXT: = default;
+ int Field;
+};
+
+struct ColonInComment {
+ ColonInComment(const ColonInComment &Other) /* : */ : Field(Other.Field) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ColonInComment(const ColonInComment &Other) /* : */ = default;
+ int Field;
+};
+
+// No members or bases (in particular, no colon).
+struct Empty {
+ Empty(const Empty &Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: Empty(const Empty &Other) = default;
+ Empty &operator=(const Empty &);
+};
+Empty &Empty::operator=(const Empty &Other) { return *this; }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use '= default'
+// CHECK-FIXES: Empty &Empty::operator=(const Empty &Other) = default;
+
+// Bit fields.
+struct BF {
+ BF() = default;
+ BF(const BF &Other) : Field1(Other.Field1), Field2(Other.Field2), Field3(Other.Field3),
+ Field4(Other.Field4) {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ // CHECK-FIXES: BF(const BF &Other) {{$}}
+ // CHECK-FIXES: = default;
+ BF &operator=(const BF &);
+
+ unsigned Field1 : 3;
+ int : 7;
+ char Field2 : 6;
+ int : 0;
+ int Field3 : 24;
+ unsigned char Field4;
+};
+BF &BF::operator=(const BF &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ Field3 = Other.Field3;
+ Field4 = Other.Field4;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-7]]:9: warning: use '= default'
+// CHECK-FIXES: BF &BF::operator=(const BF &Other) = default;
+
+// Base classes.
+struct BC : IL, OL, BF {
+ BC(const BC &Other) : IL(Other), OL(Other), BF(Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: BC(const BC &Other) = default;
+ BC &operator=(const BC &Other);
+};
+BC &BC::operator=(const BC &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ BF::operator=(Other);
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:9: warning: use '= default'
+// CHECK-FIXES: BC &BC::operator=(const BC &Other) = default;
+
+// Base classes with member.
+struct BCWM : IL, OL {
+ BCWM(const BCWM &Other) : IL(Other), OL(Other), Bf(Other.Bf) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: BCWM(const BCWM &Other) = default;
+ BCWM &operator=(const BCWM &);
+ BF Bf;
+};
+BCWM &BCWM::operator=(const BCWM &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ Bf = Other.Bf;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:13: warning: use '= default'
+// CHECK-FIXES: BCWM &BCWM::operator=(const BCWM &Other) = default;
+
+// Missing base class.
+struct MBC : IL, OL, BF {
+ MBC(const MBC &Other) : IL(Other), OL(Other) {}
+ MBC &operator=(const MBC &);
+};
+MBC &MBC::operator=(const MBC &Other) {
+ IL::operator=(Other);
+ OL::operator=(Other);
+ return *this;
+}
+
+// Base classes, incorrect parameter.
+struct BCIP : BCWM, BF {
+ BCIP(const BCIP &Other) : BCWM(Other), BF(Other.Bf) {}
+ BCIP &operator=(const BCIP &);
+};
+BCIP &BCIP::operator=(const BCIP &Other) {
+ BCWM::operator=(Other);
+ BF::operator=(Other.Bf);
+ return *this;
+}
+
+// Virtual base classes.
+struct VA : virtual OL {};
+struct VB : virtual OL {};
+struct VBC : VA, VB, virtual OL {
+ // OL is the first thing that is going to be initialized, despite the fact
+ // that it is the last in the list of bases, because it is virtual and there
+ // is a virtual OL at the beginning of VA (which is the same).
+ VBC(const VBC &Other) : OL(Other), VA(Other), VB(Other) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: VBC(const VBC &Other) = default;
+ VBC &operator=(const VBC &Other);
+};
+VBC &VBC::operator=(const VBC &Other) {
+ OL::operator=(Other);
+ VA::operator=(Other);
+ VB::operator=(Other);
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:11: warning: use '= default'
+// CHECK-FIXES: VBC &VBC::operator=(const VBC &Other) = default;
+
+// Indirect base.
+struct IB : VBC {
+ IB(const IB &Other) : OL(Other), VBC(Other) {}
+ IB &operator=(const IB &);
+};
+IB &IB::operator=(const IB &Other) {
+ OL::operator=(Other);
+ VBC::operator=(Other);
+ return *this;
+}
+
+// Class template.
+template <class T>
+struct Template {
+ Template() = default;
+ Template(const Template &Other) : Field(Other.Field) {}
+ Template &operator=(const Template &Other);
+ void foo(const T &t);
+ int Field;
+};
+template <class T>
+Template<T> &Template<T>::operator=(const Template<T> &Other) {
+ Field = Other.Field;
+ return *this;
+}
+Template<int> T1;
+
+// Dependent types.
+template <class T>
+struct DT1 {
+ DT1() = default;
+ DT1(const DT1 &Other) : Field(Other.Field) {}
+ DT1 &operator=(const DT1 &);
+ T Field;
+};
+template <class T>
+DT1<T> &DT1<T>::operator=(const DT1<T> &Other) {
+ Field = Other.Field;
+ return *this;
+}
+DT1<int> Dt1;
+
+template <class T>
+struct DT2 {
+ DT2() = default;
+ DT2(const DT2 &Other) : Field(Other.Field), Dependent(Other.Dependent) {}
+ DT2 &operator=(const DT2 &);
+ T Field;
+ typename T::TT Dependent;
+};
+template <class T>
+DT2<T> &DT2<T>::operator=(const DT2<T> &Other) {
+ Field = Other.Field;
+ Dependent = Other.Dependent;
+ return *this;
+}
+struct T {
+ typedef int TT;
+};
+DT2<T> Dt2;
+
+// Default arguments.
+struct DA {
+ DA(int Int);
+ DA(const DA &Other = DA(0)) : Field1(Other.Field1), Field2(Other.Field2) {}
+ DA &operator=(const DA &);
+ int Field1;
+ char Field2;
+};
+// Overloaded operator= cannot have a default argument.
+DA &DA::operator=(const DA &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: use '= default'
+// CHECK-FIXES: DA &DA::operator=(const DA &Other) = default;
+
+struct DA2 {
+ // Can be used as copy-constructor but cannot be explicitly defaulted.
+ DA2(const DA &Other, int Def = 0) {}
+};
+
+// Default initialization.
+struct DI {
+ DI(const DI &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ int Field1;
+ int Field2 = 0;
+ int Fiedl3;
+};
+
+// Statement inside body.
+void foo();
+struct SIB {
+ SIB(const SIB &Other) : Field(Other.Field) { foo(); }
+ SIB &operator=(const SIB &);
+ int Field;
+};
+SIB &SIB::operator=(const SIB &Other) {
+ Field = Other.Field;
+ foo();
+ return *this;
+}
+
+// Comment inside body.
+struct CIB {
+ CIB(const CIB &Other) : Field(Other.Field) { /* Don't erase this */
+ }
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+ CIB &operator=(const CIB &);
+ int Field;
+};
+CIB &CIB::operator=(const CIB &Other) {
+ Field = Other.Field;
+ // FIXME: don't erase this comment.
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:11: warning: use '= default'
+// CHECK-FIXES: CIB &CIB::operator=(const CIB &Other) = default;
+
+// Take non-const reference as argument.
+struct NCRef {
+ NCRef(NCRef &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: NCRef(NCRef &Other) = default;
+ NCRef &operator=(NCRef &);
+ int Field1, Field2;
+};
+NCRef &NCRef::operator=(NCRef &Other) {
+ Field1 = Other.Field1;
+ Field2 = Other.Field2;
+ return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:15: warning: use '= default'
+// CHECK-FIXES: NCRef &NCRef::operator=(NCRef &Other) = default;
+
+// Already defaulted.
+struct IAD {
+ IAD(const IAD &Other) = default;
+ IAD &operator=(const IAD &Other) = default;
+};
+
+struct OAD {
+ OAD(const OAD &Other);
+ OAD &operator=(const OAD &);
+};
+OAD::OAD(const OAD &Other) = default;
+OAD &OAD::operator=(const OAD &Other) = default;
+
+// Deleted.
+struct ID {
+ ID(const ID &Other) = delete;
+ ID &operator=(const ID &Other) = delete;
+};
+
+// Non-reference parameter.
+struct NRef {
+ NRef &operator=(NRef Other);
+ int Field1;
+};
+NRef &NRef::operator=(NRef Other) {
+ Field1 = Other.Field1;
+ return *this;
+}
+
+// RValue reference parameter.
+struct RVR {
+ RVR(RVR &&Other) {}
+ RVR &operator=(RVR &&);
+};
+RVR &RVR::operator=(RVR &&Other) { return *this; }
+
+// Similar function.
+struct SF {
+ SF &foo(const SF &);
+ int Field1;
+};
+SF &SF::foo(const SF &Other) {
+ Field1 = Other.Field1;
+ return *this;
+}
+
+// No return.
+struct NR {
+ NR &operator=(const NR &);
+};
+NR &NR::operator=(const NR &Other) {}
+
+// Return misplaced.
+struct RM {
+ RM &operator=(const RM &);
+ int Field;
+};
+RM &RM::operator=(const RM &Other) {
+ return *this;
+ Field = Other.Field;
+}
+
+// Wrong return value.
+struct WRV {
+ WRV &operator=(WRV &);
+};
+WRV &WRV::operator=(WRV &Other) {
+ return Other;
+}
+
+// Wrong return type.
+struct WRT : IL {
+ IL &operator=(const WRT &);
+};
+IL &WRT::operator=(const WRT &Other) {
+ return *this;
+}
+
+// Try-catch.
+struct ITC {
+ ITC(const ITC &Other)
+ try : Field(Other.Field) {
+ } catch (...) {
+ }
+ ITC &operator=(const ITC &Other) try {
+ Field = Other.Field;
+ } catch (...) {
+ }
+ int Field;
+};
+
+struct OTC {
+ OTC(const OTC &);
+ OTC &operator=(const OTC &);
+ int Field;
+};
+OTC::OTC(const OTC &Other) try : Field(Other.Field) {
+} catch (...) {
+}
+OTC &OTC::operator=(const OTC &Other) try {
+ Field = Other.Field;
+} catch (...) {
+}
+
+// FIXME: the check is not able to detect exception specification.
+// noexcept(true).
+struct NET {
+ // This is the default.
+ //NET(const NET &Other) noexcept {}
+ NET &operator=(const NET &Other) noexcept;
+};
+//NET &NET::operator=(const NET &Other) noexcept { return *this; }
+
+// noexcept(false).
+struct NEF {
+ // This is the default.
+ //NEF(const NEF &Other) noexcept(false) {}
+ NEF &operator=(const NEF &Other) noexcept(false);
+};
+//NEF &NEF::operator=(const NEF &Other) noexcept(false) { return *this; }
+
+#define STRUCT_WITH_COPY_CONSTRUCT(_base, _type) \
+ struct _type { \
+ _type(const _type &v) : value(v.value) {} \
+ _base value; \
+ };
+
+STRUCT_WITH_COPY_CONSTRUCT(unsigned char, Hex8CopyConstruct)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy constructor
+// CHECK-MESSAGES: :[[@LINE-6]]:44: note:
+
+#define STRUCT_WITH_COPY_ASSIGN(_base, _type) \
+ struct _type { \
+ _type &operator=(const _type &rhs) { \
+ value = rhs.value; \
+ return *this; \
+ } \
+ _base value; \
+ };
+
+STRUCT_WITH_COPY_ASSIGN(unsigned char, Hex8CopyAssign)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy-assignment operator
+// CHECK-MESSAGES: :[[@LINE-9]]:40: note:
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,modernize-use-equals-default -- -std=c++11 -fdelayed-template-parsing -fexceptions | count 0
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+template <typename Ty>
+struct S {
+ S<Ty>& operator=(const S<Ty>&) { return *this; }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions
+
+// Out of line definition.
+class OL {
+public:
+ OL();
+ ~OL();
+};
+
+OL::OL() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial default constructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::OL() = default;
+OL::~OL() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial destructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::~OL() = default;
+
+// Inline definitions.
+class IL {
+public:
+ IL() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: IL() = default;
+ ~IL() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ~IL() = default;
+};
+
+// Non-empty body.
+void f();
+class NE {
+public:
+ NE() { f(); }
+ ~NE() { f(); }
+};
+
+// Initializer or arguments.
+class IA {
+public:
+ // Constructor with initializer.
+ IA() : Field(5) {}
+ // Constructor with arguments.
+ IA(int Arg1, int Arg2) {}
+ int Field;
+};
+
+// Default member initializer
+class DMI {
+public:
+ DMI() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: DMI() = default;
+ int Field = 5;
+};
+
+// Class member
+class CM {
+public:
+ CM() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: CM() = default;
+ OL o;
+};
+
+// Private constructor/destructor.
+class Priv {
+ Priv() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: Priv() = default;
+ ~Priv() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ~Priv() = default;
+};
+
+// struct.
+struct ST {
+ ST() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ST() = default;
+ ~ST() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ST() = default;
+};
+
+// Deleted constructor/destructor.
+class Del {
+public:
+ Del() = delete;
+ ~Del() = delete;
+};
+
+// Do not remove other keywords.
+class KW {
+public:
+ explicit KW() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use '= default'
+ // CHECK-FIXES: explicit KW() = default;
+ virtual ~KW() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use '= default'
+ // CHECK-FIXES: virtual ~KW() = default;
+};
+
+// Nested class.
+struct N {
+ struct NN {
+ NN() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
+ // CHECK-FIXES: NN() = default;
+ ~NN() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
+ // CHECK-FIXES: ~NN() = default;
+ };
+ int Int;
+};
+
+// Class template.
+template <class T>
+class Temp {
+public:
+ Temp() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: Temp() = default;
+ ~Temp() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ~Temp() = default;
+};
+
+// Class template out of line with explicit instantiation.
+template <class T>
+class TempODef {
+public:
+ TempODef();
+ ~TempODef();
+};
+
+template <class T>
+TempODef<T>::TempODef() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default'
+// CHECK-FIXES: TempODef<T>::TempODef() = default;
+template <class T>
+TempODef<T>::~TempODef() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default'
+// CHECK-FIXES: TempODef<T>::~TempODef() = default;
+
+template class TempODef<int>;
+template class TempODef<double>;
+
+// Non user-provided constructor/destructor.
+struct Imp {
+ int Int;
+};
+void g() {
+ Imp *PtrImp = new Imp();
+ PtrImp->~Imp();
+ delete PtrImp;
+}
+
+// Already using default.
+struct IDef {
+ IDef() = default;
+ ~IDef() = default;
+};
+struct ODef {
+ ODef();
+ ~ODef();
+};
+ODef::ODef() = default;
+ODef::~ODef() = default;
+
+// Delegating constructor and overriden destructor.
+struct DC : KW {
+ DC() : KW() {}
+ ~DC() override {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+ // CHECK-FIXES: ~DC() override = default;
+};
+
+struct Comments {
+ Comments() {
+ // Don't erase comments inside the body.
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+ ~Comments() {
+ // Don't erase comments inside the body.
+ }
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+};
+
+// Try-catch.
+struct ITC {
+ ITC() try {} catch(...) {}
+ ~ITC() try {} catch(...) {}
+};
+
+struct OTC {
+ OTC();
+ ~OTC();
+};
+OTC::OTC() try {} catch(...) {}
+OTC::~OTC() try {} catch(...) {}
+
+#define STRUCT_WITH_DEFAULT(_base, _type) \
+ struct _type { \
+ _type() {} \
+ _base value; \
+ };
+
+STRUCT_WITH_DEFAULT(unsigned char, Hex8Default)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor
+// CHECK-MESSAGES: :[[@LINE-6]]:13: note:
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-equals-delete %t
+
+struct PositivePrivate {
+private:
+ PositivePrivate();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivate() = delete;
+ PositivePrivate(const PositivePrivate &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivate(const PositivePrivate &) = delete;
+ PositivePrivate &operator=(const PositivePrivate &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivate &operator=(const PositivePrivate &) = delete;
+ PositivePrivate(PositivePrivate &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivate(PositivePrivate &&) = delete;
+ PositivePrivate &operator=(PositivePrivate &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivate &operator=(PositivePrivate &&) = delete;
+ ~PositivePrivate();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: ~PositivePrivate() = delete;
+};
+
+template<typename T>
+struct PositivePrivateTemplate {
+private:
+ PositivePrivateTemplate();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivateTemplate() = delete;
+ PositivePrivateTemplate(const PositivePrivateTemplate &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivateTemplate(const PositivePrivateTemplate &) = delete;
+ PositivePrivateTemplate &operator=(const PositivePrivateTemplate &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivateTemplate &operator=(const PositivePrivateTemplate &) = delete;
+ PositivePrivateTemplate(PositivePrivateTemplate &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivateTemplate(PositivePrivateTemplate &&) = delete;
+ PositivePrivateTemplate &operator=(PositivePrivateTemplate &&);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositivePrivateTemplate &operator=(PositivePrivateTemplate &&) = delete;
+ ~PositivePrivateTemplate();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: ~PositivePrivateTemplate() = delete;
+};
+
+template struct PositivePrivateTemplate<int>;
+template struct PositivePrivateTemplate<char>;
+
+struct NegativePublic {
+ NegativePublic(const NegativePublic &);
+};
+
+struct NegativeProtected {
+protected:
+ NegativeProtected(const NegativeProtected &);
+};
+
+struct PositiveInlineMember {
+ int foo() { return 0; }
+
+private:
+ PositiveInlineMember(const PositiveInlineMember &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositiveInlineMember(const PositiveInlineMember &) = delete;
+};
+
+struct PositiveOutOfLineMember {
+ int foo();
+
+private:
+ PositiveOutOfLineMember(const PositiveOutOfLineMember &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositiveOutOfLineMember(const PositiveOutOfLineMember &) = delete;
+};
+
+int PositiveOutOfLineMember::foo() { return 0; }
+
+struct PositiveAbstractMember {
+ virtual int foo() = 0;
+
+private:
+ PositiveAbstractMember(const PositiveAbstractMember &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositiveAbstractMember(const PositiveAbstractMember &) = delete;
+};
+
+struct NegativeMemberNotImpl {
+ int foo();
+
+private:
+ NegativeMemberNotImpl(const NegativeMemberNotImpl &);
+};
+
+struct NegativeStaticMemberNotImpl {
+ static int foo();
+
+private:
+ NegativeStaticMemberNotImpl(const NegativeStaticMemberNotImpl &);
+};
+
+struct NegativeInline {
+private:
+ NegativeInline(const NegativeInline &) {}
+};
+
+struct NegativeOutOfLine {
+private:
+ NegativeOutOfLine(const NegativeOutOfLine &);
+};
+
+NegativeOutOfLine::NegativeOutOfLine(const NegativeOutOfLine &) {}
+
+struct NegativeConstructNotImpl {
+ NegativeConstructNotImpl();
+
+private:
+ NegativeConstructNotImpl(const NegativeConstructNotImpl &);
+};
+
+struct PositiveDefaultedConstruct {
+ PositiveDefaultedConstruct() = default;
+
+private:
+ PositiveDefaultedConstruct(const PositiveDefaultedConstruct &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositiveDefaultedConstruct(const PositiveDefaultedConstruct &) = delete;
+};
+
+struct PositiveDeletedConstruct {
+ PositiveDeletedConstruct() = delete;
+
+private:
+ PositiveDeletedConstruct(const PositiveDeletedConstruct &);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+ // CHECK-FIXES: PositiveDeletedConstruct(const PositiveDeletedConstruct &) = delete;
+};
+
+struct NegativeDefaulted {
+private:
+ NegativeDefaulted(const NegativeDefaulted &) = default;
+};
+
+struct PrivateDeleted {
+private:
+ PrivateDeleted(const PrivateDeleted &) = delete;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
+};
+
+struct ProtectedDeleted {
+protected:
+ ProtectedDeleted(const ProtectedDeleted &) = delete;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
+};
+
+struct PublicDeleted {
+public:
+ PublicDeleted(const PublicDeleted &) = delete;
+};
+
+#define M1 \
+ struct PrivateDeletedMacro { \
+ private: \
+ PrivateDeletedMacro(const PrivateDeletedMacro &) = delete; \
+ }; \
+ struct ProtectedDeletedMacro { \
+ protected: \
+ ProtectedDeletedMacro(const ProtectedDeletedMacro &) = delete; \
+ }
+
+M1;
+
+#define DISALLOW_COPY_AND_ASSIGN(name) \
+ name(const name &) = delete; \
+ void operator=(const name &) = delete
+
+struct PrivateDeletedMacro2 {
+private:
+ DISALLOW_COPY_AND_ASSIGN(PrivateDeletedMacro2);
+};
+
+struct ProtectedDeletedMacro2 {
+protected:
+ DISALLOW_COPY_AND_ASSIGN(ProtectedDeletedMacro2);
+};
+
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \
+// RUN: -- -std=c++11 -fexceptions
+
+// Example definition of NOEXCEPT -- simplified test to see if noexcept is supported.
+#if (__has_feature(cxx_noexcept))
+#define NOEXCEPT noexcept
+#else
+#define NOEXCEPT throw()
+#endif
+
+void bar() throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() NOEXCEPT {}
+
+// Should not trigger a FixItHint, since macros only support noexcept, and this
+// case throws.
+class A {};
+class B {};
+void foobar() throw(A, B);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+
+// Should not trigger a replacement.
+void foo() noexcept(true);
+
+struct Z {
+ void operator delete(void *ptr) throw();
+ void operator delete[](void *ptr) throw(int);
+ ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) NOEXCEPT;
+// CHECK-FIXES: void operator delete[](void *ptr) throw(int);
+// CHECK-FIXES: ~Z() throw(int) {}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.UseNoexceptFalse, value: 0}]}" \
+// RUN: -- -std=c++11 -fexceptions
+
+class A {};
+class B {};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() ;
+
+void k() throw(int(int));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void k() ;
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void foobar()
+
+void baz(int = (throw A(), 0)) throw(A, B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void baz(int = (throw A(), 0)) {}
+
+void g(void (*fp)(void) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void g(void (*fp)(void) noexcept);
+
+void f(void (*fp)(void) throw(int)) throw(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void f(void (*fp)(void) ) ;
+
+#define THROW throw
+void h(void (*fp)(void) THROW(int)) THROW(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void h(void (*fp)(void) ) ;
+
+void j() throw(int(int) throw(void(void) throw(int)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void j() ;
+
+class Y {
+ Y() throw() = default;
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: Y() noexcept = default;
+
+struct Z {
+ void operator delete(void *ptr) throw();
+ void operator delete[](void *ptr) throw(int);
+ ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) noexcept;
+// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false);
+// CHECK-FIXES: ~Z() noexcept(false) {}
+
+struct S {
+ void f() throw();
+};
+void f(void (S::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f() noexcept;
+// CHECK-FIXES: void f(void (S::*)() noexcept);
+
+typedef void (*fp)(void (*fp2)(int) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept);
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
+
+// Should not trigger a replacement.
+void bad()
+#if !__has_feature(cxx_noexcept)
+ throw()
+#endif
+ ;
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN: -- -std=c++11 -fexceptions
+
+class A {};
+class B {};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+template <typename T>
+void foo() throw();
+void footest() { foo<int>(); foo<double>(); }
+// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() noexcept(false);
+
+void k() throw(int(int));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void k() noexcept(false);
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foobar() noexcept(false)
+
+void baz(int = (throw A(), 0)) throw(A, B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void baz(int = (throw A(), 0)) noexcept(false) {}
+
+void g(void (*fp)(void) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void g(void (*fp)(void) noexcept);
+
+void f(void (*fp)(void) throw(int)) throw(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f(void (*fp)(void) noexcept(false)) noexcept(false);
+
+#define THROW throw
+void h(void (*fp)(void) THROW(int)) THROW(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void h(void (*fp)(void) noexcept(false)) noexcept(false);
+
+void j() throw(int(int) throw(void(void) throw(int)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void j() noexcept(false);
+
+class Y {
+ Y() throw() = default;
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: Y() noexcept = default;
+
+struct Z {
+ void operator delete(void *ptr) throw();
+ void operator delete[](void *ptr) throw(int);
+ ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) noexcept;
+// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false);
+// CHECK-FIXES: ~Z() noexcept(false) {}
+
+struct S {
+ void f() throw();
+};
+void f(void (S::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f() noexcept;
+// CHECK-FIXES: void f(void (S::*)() noexcept);
+
+template <typename T>
+struct ST {
+ void foo() throw();
+};
+template <typename T>
+void ft(void (ST<T>::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-4]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+// CHECK-FIXES: void ft(void (ST<T>::*)() noexcept);
+
+typedef void (*fp)(void (*fp2)(int) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept);
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
+
+// Should not trigger a replacement.
+void bad()
+#if !__has_feature(cxx_noexcept)
+ throw()
+#endif
+ ;
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- -- \
+// RUN: -std=c++98 -Wno-non-literal-null-conversion
+//
+// Some parts of the test (e.g. assignment of `const int` to `int *`) fail in
+// C++11, so we need to run the test in C++98 mode.
+
+const unsigned int g_null = 0;
+#define NULL 0
+
+void test_assignment() {
+ int *p1 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr [modernize-use-nullptr]
+ // CHECK-FIXES: int *p1 = nullptr;
+ p1 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+ // CHECK-FIXES: p1 = nullptr;
+
+ int *p2 = NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+ // CHECK-FIXES: int *p2 = nullptr;
+
+ p2 = p1;
+ // CHECK-FIXES: p2 = p1;
+
+ const int null = 0;
+ int *p3 = null;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+ // CHECK-FIXES: int *p3 = nullptr;
+
+ p3 = NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+ // CHECK-FIXES: p3 = nullptr;
+
+ int *p4 = p3;
+ // CHECK-FIXES: int *p4 = p3;
+
+ p4 = null;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+ // CHECK-FIXES: p4 = nullptr;
+
+ int i1 = 0;
+
+ int i2 = NULL;
+
+ int i3 = null;
+
+ int *p5, *p6, *p7;
+ p5 = p6 = p7 = NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+ // CHECK-FIXES: p5 = p6 = p7 = nullptr;
+}
+
+struct Foo {
+ Foo(int *p = NULL) : m_p1(p) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+ // CHECK-FIXES: Foo(int *p = nullptr) : m_p1(p) {}
+
+ void bar(int *p = 0) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use nullptr
+ // CHECK-FIXES: void bar(int *p = nullptr) {}
+
+ void baz(int i = 0) {}
+
+ int *m_p1;
+ static int *m_p2;
+};
+
+int *Foo::m_p2 = NULL;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+// CHECK-FIXES: int *Foo::m_p2 = nullptr;
+
+template <typename T>
+struct Bar {
+ Bar(T *p) : m_p(p) {
+ m_p = static_cast<T*>(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+ // CHECK-FIXES: m_p = static_cast<T*>(nullptr);
+
+ m_p = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+ // CHECK-FIXES: m_p = static_cast<T*>(nullptr);
+
+ m_p = static_cast<T*>(p ? p : static_cast<void*>(g_null));
+ // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: use nullptr
+ // CHECK-FIXES: m_p = static_cast<T*>(p ? p : static_cast<void*>(nullptr));
+
+ T *p2 = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+ // CHECK-FIXES: T *p2 = static_cast<T*>(nullptr);
+
+ m_p = NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use nullptr
+ // CHECK-FIXES: m_p = nullptr;
+
+ int i = static_cast<int>(0.f);
+ T *i2 = static_cast<int>(0.f);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+ // CHECK-FIXES: T *i2 = nullptr;
+ }
+
+ T *m_p;
+};
+
+struct Baz {
+ Baz() : i(0) {}
+ int i;
+};
+
+void test_cxx_cases() {
+ Foo f(g_null);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+ // CHECK-FIXES: Foo f(nullptr);
+
+ f.bar(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+ // CHECK-FIXES: f.bar(nullptr);
+
+ f.baz(g_null);
+
+ f.m_p1 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+ // CHECK-FIXES: f.m_p1 = nullptr;
+
+ Bar<int> b(g_null);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+ // CHECK-FIXES: Bar<int> b(nullptr);
+
+ Baz b2;
+ int Baz::*memptr(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use nullptr
+ // CHECK-FIXES: int Baz::*memptr(nullptr);
+
+ memptr = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+ // CHECK-FIXES: memptr = nullptr;
+}
+
+void test_function_default_param1(void *p = 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param1(void *p = nullptr);
+
+void test_function_default_param2(void *p = NULL);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param2(void *p = nullptr);
+
+void test_function_default_param3(void *p = g_null);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param3(void *p = nullptr);
+
+void test_function(int *p) {}
+
+void test_function_no_ptr_param(int i) {}
+
+void test_function_call() {
+ test_function(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+ // CHECK-FIXES: test_function(nullptr);
+
+ test_function(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+ // CHECK-FIXES: test_function(nullptr);
+
+ test_function(g_null);
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+ // CHECK-FIXES: test_function(nullptr);
+
+ test_function_no_ptr_param(0);
+}
+
+char *test_function_return1() {
+ return 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+ // CHECK-FIXES: return nullptr;
+}
+
+void *test_function_return2() {
+ return NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+ // CHECK-FIXES: return nullptr;
+}
+
+long *test_function_return3() {
+ return g_null;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+ // CHECK-FIXES: return nullptr;
+}
+
+int test_function_return4() {
+ return 0;
+}
+
+int test_function_return5() {
+ return NULL;
+}
+
+int test_function_return6() {
+ return g_null;
+}
+
+int *test_function_return_cast1() {
+ return(int)0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+ // CHECK-FIXES: return nullptr;
+}
+
+int *test_function_return_cast2() {
+#define RET return
+ RET(int)0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use nullptr
+ // CHECK-FIXES: RET nullptr;
+#undef RET
+}
+
+// Test parentheses expressions resulting in a nullptr.
+int *test_parentheses_expression1() {
+ return(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+ // CHECK-FIXES: return(nullptr);
+}
+
+int *test_parentheses_expression2() {
+ return(int(0.f));
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+ // CHECK-FIXES: return(nullptr);
+}
+
+int *test_nested_parentheses_expression() {
+ return((((0))));
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+ // CHECK-FIXES: return((((nullptr))));
+}
+
+void *test_parentheses_explicit_cast() {
+ return(static_cast<void*>(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+ // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+void *test_parentheses_explicit_cast_sequence1() {
+ return(static_cast<void*>(static_cast<int*>((void*)NULL)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+ // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+void *test_parentheses_explicit_cast_sequence2() {
+ return(static_cast<void*>(reinterpret_cast<int*>((float*)int(0.f))));
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+ // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+// Test explicit cast expressions resulting in nullptr.
+struct Bam {
+ Bam(int *a) {}
+ Bam(float *a) {}
+ Bam operator=(int *a) { return Bam(a); }
+ Bam operator=(float *a) { return Bam(a); }
+};
+
+void ambiguous_function(int *a) {}
+void ambiguous_function(float *a) {}
+void const_ambiguous_function(const int *p) {}
+void const_ambiguous_function(const float *p) {}
+
+void test_explicit_cast_ambiguous1() {
+ ambiguous_function((int*)0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use nullptr
+ // CHECK-FIXES: ambiguous_function((int*)nullptr);
+}
+
+void test_explicit_cast_ambiguous2() {
+ ambiguous_function((int*)(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use nullptr
+ // CHECK-FIXES: ambiguous_function((int*)nullptr);
+}
+
+void test_explicit_cast_ambiguous3() {
+ ambiguous_function(static_cast<int*>(reinterpret_cast<int*>((float*)0)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: use nullptr
+ // CHECK-FIXES: ambiguous_function(static_cast<int*>(nullptr));
+}
+
+Bam test_explicit_cast_ambiguous4() {
+ return(((int*)(0)));
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+ // CHECK-FIXES: return(((int*)nullptr));
+}
+
+void test_explicit_cast_ambiguous5() {
+ // Test for ambiguous overloaded constructors.
+ Bam k((int*)(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use nullptr
+ // CHECK-FIXES: Bam k((int*)nullptr);
+
+ // Test for ambiguous overloaded operators.
+ k = (int*)0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+ // CHECK-FIXES: k = (int*)nullptr;
+}
+
+void test_const_pointers_abiguous() {
+ const_ambiguous_function((int*)0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use nullptr
+ // CHECK-FIXES: const_ambiguous_function((int*)nullptr);
+}
+
+// Test where the implicit cast to null is surrounded by another implict cast
+// with possible explict casts in-between.
+void test_const_pointers() {
+ const int *const_p1 = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+ // CHECK-FIXES: const int *const_p1 = nullptr;
+ const int *const_p2 = NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+ // CHECK-FIXES: const int *const_p2 = nullptr;
+ const int *const_p3 = (int)0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+ // CHECK-FIXES: const int *const_p3 = nullptr;
+ const int *const_p4 = (int)0.0f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+ // CHECK-FIXES: const int *const_p4 = nullptr;
+ const int *const_p5 = (int*)0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use nullptr
+ // CHECK-FIXES: const int *const_p5 = (int*)nullptr;
+ int *t;
+ const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:69: warning: use nullptr
+ // CHECK-FIXES: const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(nullptr));
+}
+
+void test_nested_implicit_cast_expr() {
+ int func0(void*, void*);
+ int func1(int, void*, void*);
+
+ (double)func1(0, 0, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use nullptr
+ // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: use nullptr
+ // CHECK-FIXES: (double)func1(0, nullptr, nullptr);
+ (double)func1(func0(0, 0), 0, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use nullptr
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: use nullptr
+ // CHECK-MESSAGES: :[[@LINE-3]]:30: warning: use nullptr
+ // CHECK-MESSAGES: :[[@LINE-4]]:33: warning: use nullptr
+ // CHECK-FIXES: (double)func1(func0(nullptr, nullptr), nullptr, nullptr);
+}
+
+// FIXME: currently, the check doesn't work as it should with templates.
+template<typename T>
+class A {
+ public:
+ A(T *p = NULL) {}
+
+ void f() {
+ Ptr = NULL;
+ }
+ T *Ptr;
+};
+
+template<typename T>
+T *f2(T *a = NULL) {
+ return a ? a : NULL;
+}
--- /dev/null
+// RUN: clang-tidy %s -checks=-*,modernize-use-nullptr -- | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+#define NULL 0
+void f(void) {
+ char *str = NULL; // ok
+ (void)str;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-nullptr.NullMacros, value: 'MY_NULL,NULL'}]}" \
+// RUN: -- -std=c++11
+
+#define NULL 0
+
+namespace std {
+
+typedef decltype(nullptr) nullptr_t;
+
+} // namespace std
+
+// Just to make sure make_null() could have side effects.
+void external();
+
+std::nullptr_t make_null() {
+ external();
+ return nullptr;
+}
+
+void func() {
+ void *CallTest = make_null();
+
+ int var = 1;
+ void *CommaTest = (var+=2, make_null());
+
+ int *CastTest = static_cast<int*>(make_null());
+}
+
+void dummy(int*) {}
+void side_effect() {}
+
+#define MACRO_EXPANSION_HAS_NULL \
+ void foo() { \
+ dummy(0); \
+ dummy(NULL); \
+ side_effect(); \
+ }
+
+MACRO_EXPANSION_HAS_NULL;
+#undef MACRO_EXPANSION_HAS_NULL
+
+
+void test_macro_expansion1() {
+#define MACRO_EXPANSION_HAS_NULL \
+ dummy(NULL); \
+ side_effect();
+
+ MACRO_EXPANSION_HAS_NULL;
+
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+// Test macro expansion with cast sequence, PR15572.
+void test_macro_expansion2() {
+#define MACRO_EXPANSION_HAS_NULL \
+ dummy((int*)0); \
+ side_effect();
+
+ MACRO_EXPANSION_HAS_NULL;
+
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+void test_macro_expansion3() {
+#define MACRO_EXPANSION_HAS_NULL \
+ dummy(NULL); \
+ side_effect();
+
+#define OUTER_MACRO \
+ MACRO_EXPANSION_HAS_NULL; \
+ side_effect();
+
+ OUTER_MACRO;
+
+#undef OUTER_MACRO
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+void test_macro_expansion4() {
+#define MY_NULL NULL
+ int *p = MY_NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr [modernize-use-nullptr]
+ // CHECK-FIXES: int *p = nullptr;
+#undef MY_NULL
+}
+
+#define IS_EQ(x, y) if (x != y) return;
+void test_macro_args() {
+ int i = 0;
+ int *Ptr;
+
+ IS_EQ(static_cast<int*>(0), Ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+ // CHECK-FIXES: IS_EQ(static_cast<int*>(nullptr), Ptr);
+
+ IS_EQ(0, Ptr); // literal
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+ // CHECK-FIXES: IS_EQ(nullptr, Ptr);
+
+ IS_EQ(NULL, Ptr); // macro
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+ // CHECK-FIXES: IS_EQ(nullptr, Ptr);
+
+ // These are ok since the null literal is not spelled within a macro.
+#define myassert(x) if (!(x)) return;
+ myassert(0 == Ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+ // CHECK-FIXES: myassert(nullptr == Ptr);
+
+ myassert(NULL == Ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+ // CHECK-FIXES: myassert(nullptr == Ptr);
+
+ // These are bad as the null literal is buried in a macro.
+#define BLAH(X) myassert(0 == (X));
+#define BLAH2(X) myassert(NULL == (X));
+ BLAH(Ptr);
+ BLAH2(Ptr);
+
+ // Same as above but testing extra macro expansion.
+#define EXPECT_NULL(X) IS_EQ(0, X);
+#define EXPECT_NULL2(X) IS_EQ(NULL, X);
+ EXPECT_NULL(Ptr);
+ EXPECT_NULL2(Ptr);
+
+ // Almost the same as above but now null literal is not in a macro so ok
+ // to transform.
+#define EQUALS_PTR(X) IS_EQ(X, Ptr);
+ EQUALS_PTR(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+ // CHECK-FIXES: EQUALS_PTR(nullptr);
+ EQUALS_PTR(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+ // CHECK-FIXES: EQUALS_PTR(nullptr);
+
+ // Same as above but testing extra macro expansion.
+#define EQUALS_PTR_I(X) EQUALS_PTR(X)
+ EQUALS_PTR_I(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+ // CHECK-FIXES: EQUALS_PTR_I(nullptr);
+ EQUALS_PTR_I(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+ // CHECK-FIXES: EQUALS_PTR_I(nullptr);
+
+ // Ok since null literal not within macro. However, now testing macro
+ // used as arg to another macro.
+#define decorate(EXPR) side_effect(); EXPR;
+ decorate(IS_EQ(NULL, Ptr));
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+ // CHECK-FIXES: decorate(IS_EQ(nullptr, Ptr));
+ decorate(IS_EQ(0, Ptr));
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+ // CHECK-FIXES: decorate(IS_EQ(nullptr, Ptr));
+
+ // This macro causes a NullToPointer cast to happen where 0 is assigned to z
+ // but the 0 literal cannot be replaced because it is also used as an
+ // integer in the comparison.
+#define INT_AND_PTR_USE(X) do { int *z = X; if (X == 4) break; } while(false)
+ INT_AND_PTR_USE(0);
+
+ // Both uses of X in this case result in NullToPointer casts so replacement
+ // is possible.
+#define PTR_AND_PTR_USE(X) do { int *z = X; if (X != z) break; } while(false)
+ PTR_AND_PTR_USE(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+ // CHECK-FIXES: PTR_AND_PTR_USE(nullptr);
+ PTR_AND_PTR_USE(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+ // CHECK-FIXES: PTR_AND_PTR_USE(nullptr);
+
+#define OPTIONAL_CODE(...) __VA_ARGS__
+#define NOT_NULL dummy(0)
+#define CALL(X) X
+ OPTIONAL_CODE(NOT_NULL);
+ CALL(NOT_NULL);
+
+#define ENTRY(X) {X}
+ struct A {
+ int *Ptr;
+ } a[2] = {ENTRY(0), {0}};
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+ // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: use nullptr
+ // CHECK-FIXES: a[2] = {ENTRY(nullptr), {nullptr}};
+#undef ENTRY
+
+#define assert1(expr) (expr) ? 0 : 1
+#define assert2 assert1
+ int *p;
+ assert2(p == 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+ // CHECK-FIXES: assert2(p == nullptr);
+ assert2(p == NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+ // CHECK-FIXES: assert2(p == nullptr);
+#undef assert2
+#undef assert1
+
+#define ASSERT_EQ(a, b) a == b
+#define ASSERT_NULL(x) ASSERT_EQ(static_cast<void *>(NULL), x)
+ int *pp;
+ ASSERT_NULL(pp);
+ ASSERT_NULL(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use nullptr
+ // CHECK-FIXES: ASSERT_NULL(nullptr);
+#undef ASSERT_NULL
+#undef ASSERT_EQ
+}
+
+// One of the ancestor of the cast is a NestedNameSpecifierLoc.
+class NoDef;
+char function(NoDef *p);
+#define F(x) (sizeof(function(x)) == 1)
+template<class T, T t>
+class C {};
+C<bool, F(0)> c;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use nullptr
+// CHECK-FIXES: C<bool, F(nullptr)> c;
+#undef F
+
+// Test default argument expression.
+struct D {
+ explicit D(void *t, int *c = NULL) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use nullptr
+ // CHECK-FIXES: explicit D(void *t, int *c = nullptr) {}
+};
+
+void test_default_argument() {
+ D(nullptr);
+}
+
+// Test on two neighbour CXXDefaultArgExprs nodes.
+typedef unsigned long long uint64;
+struct ZZ {
+ explicit ZZ(uint64, const uint64* = NULL) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: use nullptr
+// CHECK-FIXES: explicit ZZ(uint64, const uint64* = nullptr) {}
+ operator bool() { return true; }
+};
+
+uint64 Hash(uint64 seed = 0) { return 0; }
+
+void f() {
+ bool a;
+ a = ZZ(Hash());
+}
+
+// Test on ignoring substituted template types.
+template<typename T>
+class TemplateClass {
+ public:
+ explicit TemplateClass(int a, T default_value = 0) {}
+
+ void h(T *default_value = 0) {}
+
+ void f(int* p = 0) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+// CHECK-FIXES: void f(int* p = nullptr) {}
+};
+
+void IgnoreSubstTemplateType() {
+ TemplateClass<int*> a(1);
+}
+
+// Test on casting nullptr.
+struct G {
+ explicit G(bool, const char * = NULL) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use nullptr
+ // CHECK-FIXES: explicit G(bool, const char * = nullptr) {}
+};
+bool g(const char*);
+void test_cast_nullptr() {
+ G(g(nullptr));
+ G(g((nullptr)));
+ G(g(static_cast<char*>(nullptr)));
+ G(g(static_cast<const char*>(nullptr)));
+}
+
+// Test on recognizing multiple NULLs.
+class H {
+public:
+ H(bool);
+};
+
+#define T(expression) H(expression);
+bool h(int *, int *, int * = nullptr);
+void test_multiple_nulls() {
+ T(h(NULL, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+ T(h(NULL, nullptr));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+ T(h(nullptr, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+ T(h(nullptr, nullptr));
+ T(h(NULL, NULL, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-3]]:19: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr, nullptr));
+}
+#undef T
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -std=c++98
+
+struct Base {
+ virtual ~Base() {}
+ virtual void a();
+ virtual void b();
+};
+
+struct SimpleCases : public Base {
+public:
+ virtual ~SimpleCases();
+ // CHECK-FIXES: {{^}} virtual ~SimpleCases();
+
+ void a();
+ // CHECK-FIXES: {{^}} void a();
+
+ virtual void b();
+ // CHECK-FIXES: {{^}} virtual void b();
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -fms-extensions -std=c++11
+
+// This test is designed to test ms-extension __declspec(dllexport) attributes.
+#define EXPORT __declspec(dllexport)
+
+class Base {
+ virtual EXPORT void a();
+};
+
+class EXPORT InheritedBase {
+ virtual void a();
+};
+
+class Derived : public Base {
+ virtual EXPORT void a();
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+ // CHECK-FIXES: {{^}} EXPORT void a() override;
+};
+
+class EXPORT InheritedDerived : public InheritedBase {
+ virtual void a();
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+ // CHECK-FIXES: {{^}} void a() override;
+};
+
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -std=c++11 -fexceptions
+
+#define ABSTRACT = 0
+
+#define OVERRIDE override
+#define VIRTUAL virtual
+#define NOT_VIRTUAL
+#define NOT_OVERRIDE
+
+#define MUST_USE_RESULT __attribute__((warn_unused_result))
+#define UNUSED __attribute__((unused))
+
+struct MUST_USE_RESULT MustUseResultObject {};
+
+struct IntPair {
+ int First, Second;
+};
+
+struct Base {
+ virtual ~Base() {}
+ virtual void a();
+ virtual void b();
+ virtual void c();
+ virtual void d();
+ virtual void d2();
+ virtual void e() = 0;
+ virtual void f() = 0;
+ virtual void f2() const = 0;
+ virtual void g() = 0;
+
+ virtual void j() const;
+ virtual MustUseResultObject k();
+ virtual bool l() MUST_USE_RESULT UNUSED;
+ virtual bool n() MUST_USE_RESULT UNUSED;
+
+ virtual void m();
+ virtual void m2();
+ virtual void o() __attribute__((unused));
+
+ virtual void r() &;
+ virtual void rr() &&;
+
+ virtual void cv() const volatile;
+ virtual void cv2() const volatile;
+
+ virtual void ne() noexcept(false);
+ virtual void t() throw();
+
+ virtual void il(IntPair);
+};
+
+struct SimpleCases : public Base {
+public:
+ virtual ~SimpleCases();
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+ // CHECK-FIXES: {{^}} ~SimpleCases() override;
+
+ void a();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+ // CHECK-FIXES: {{^}} void a() override;
+
+ void b() override;
+ // CHECK-MESSAGES-NOT: warning:
+ // CHECK-FIXES: {{^}} void b() override;
+
+ virtual void c();
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void c() override;
+
+ virtual void d() override;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'override'
+ // CHECK-FIXES: {{^}} void d() override;
+
+ virtual void d2() final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'final'
+ // CHECK-FIXES: {{^}} void d2() final;
+
+ virtual void e() = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void e() override = 0;
+
+ virtual void f()=0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void f() override =0;
+
+ virtual void f2() const=0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void f2() const override =0;
+
+ virtual void g() ABSTRACT;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void g() override ABSTRACT;
+
+ virtual void j() const;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void j() const override;
+
+ virtual MustUseResultObject k(); // Has an implicit attribute.
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: prefer using
+ // CHECK-FIXES: {{^}} MustUseResultObject k() override;
+
+ virtual bool l() MUST_USE_RESULT UNUSED;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED;
+
+ virtual bool n() UNUSED MUST_USE_RESULT;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} bool n() override UNUSED MUST_USE_RESULT;
+
+ void m() override final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'override' is redundant since the function is already declared 'final'
+ // CHECK-FIXES: {{^}} void m() final;
+
+ virtual void m2() override final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' and 'override' are redundant since the function is already declared 'final'
+ // CHECK-FIXES: {{^}} void m2() final;
+
+ virtual void o() __attribute__((unused));
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void o() override __attribute__((unused));
+
+ virtual void ne() noexcept(false);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void ne() noexcept(false) override;
+
+ virtual void t() throw();
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void t() throw() override;
+};
+
+// CHECK-MESSAGES-NOT: warning:
+
+void SimpleCases::c() {}
+// CHECK-FIXES: {{^}}void SimpleCases::c() {}
+
+SimpleCases::~SimpleCases() {}
+// CHECK-FIXES: {{^}}SimpleCases::~SimpleCases() {}
+
+struct DefaultedDestructor : public Base {
+ DefaultedDestructor() {}
+ virtual ~DefaultedDestructor() = default;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+ // CHECK-FIXES: {{^}} ~DefaultedDestructor() override = default;
+};
+
+struct FinalSpecified : public Base {
+public:
+ virtual ~FinalSpecified() final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'virtual' is redundant since the function is already declared 'final'
+ // CHECK-FIXES: {{^}} ~FinalSpecified() final;
+
+ void b() final;
+ // CHECK-MESSAGES-NOT: warning:
+ // CHECK-FIXES: {{^}} void b() final;
+
+ virtual void d() final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} void d() final;
+
+ virtual void e() final = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} void e() final = 0;
+
+ virtual void j() const final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} void j() const final;
+
+ virtual bool l() final MUST_USE_RESULT UNUSED;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} bool l() final MUST_USE_RESULT UNUSED;
+};
+
+struct InlineDefinitions : public Base {
+public:
+ virtual ~InlineDefinitions() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+ // CHECK-FIXES: {{^}} ~InlineDefinitions() override {}
+
+ void a() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+ // CHECK-FIXES: {{^}} void a() override {}
+
+ void b() override {}
+ // CHECK-MESSAGES-NOT: warning:
+ // CHECK-FIXES: {{^}} void b() override {}
+
+ virtual void c()
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void c() override
+
+ virtual void d() override {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} void d() override {}
+
+ virtual void j() const
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void j() const override
+
+ virtual MustUseResultObject k() {} // Has an implicit attribute.
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: prefer using
+ // CHECK-FIXES: {{^}} MustUseResultObject k() override {}
+
+ virtual bool l() MUST_USE_RESULT UNUSED {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} bool l() override MUST_USE_RESULT UNUSED {}
+
+ virtual void r() &
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void r() & override
+
+ virtual void rr() &&
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void rr() && override
+
+ virtual void cv() const volatile
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void cv() const volatile override
+
+ virtual void cv2() const volatile // some comment
+ {}
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void cv2() const volatile override // some comment
+};
+
+struct DefaultArguments : public Base {
+ // Tests for default arguments (with initializer lists).
+ // Make sure the override fix is placed after the argument list.
+ void il(IntPair p = {1, (2 + (3))}) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+ // CHECK-FIXES: {{^}} void il(IntPair p = {1, (2 + (3))}) override {}
+};
+
+struct Macros : public Base {
+ // Tests for 'virtual' and 'override' being defined through macros. Basically
+ // give up for now.
+ NOT_VIRTUAL void a() NOT_OVERRIDE;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: annotate this
+ // CHECK-FIXES: {{^}} NOT_VIRTUAL void a() override NOT_OVERRIDE;
+
+ VIRTUAL void b() NOT_OVERRIDE;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} VIRTUAL void b() override NOT_OVERRIDE;
+
+ NOT_VIRTUAL void c() OVERRIDE;
+ // CHECK-MESSAGES-NOT: warning:
+ // CHECK-FIXES: {{^}} NOT_VIRTUAL void c() OVERRIDE;
+
+ VIRTUAL void d() OVERRIDE;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+ // CHECK-FIXES: {{^}} VIRTUAL void d() OVERRIDE;
+
+#define FUNC(return_type, name) return_type name()
+ FUNC(void, e);
+ // CHECK-FIXES: {{^}} FUNC(void, e);
+
+#define F virtual void f();
+ F
+ // CHECK-FIXES: {{^}} F
+
+ VIRTUAL void g() OVERRIDE final;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' and 'override' are redundant
+ // CHECK-FIXES: {{^}} VIRTUAL void g() final;
+};
+
+// Tests for templates.
+template <typename T> struct TemplateBase {
+ virtual void f(T t);
+};
+
+template <typename T> struct DerivedFromTemplate : public TemplateBase<T> {
+ virtual void f(T t);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void f(T t) override;
+};
+void f() { DerivedFromTemplate<int>().f(2); }
+
+template <class C>
+struct UnusedMemberInstantiation : public C {
+ virtual ~UnusedMemberInstantiation() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+ // CHECK-FIXES: {{^}} ~UnusedMemberInstantiation() override {}
+};
+struct IntantiateWithoutUse : public UnusedMemberInstantiation<Base> {};
+
+struct Base2 {
+ virtual ~Base2() {}
+ virtual void a();
+};
+
+// The OverrideAttr isn't propagated to specializations in all cases. Make sure
+// we don't add "override" a second time.
+template <int I>
+struct MembersOfSpecializations : public Base2 {
+ void a() override;
+ // CHECK-MESSAGES-NOT: warning:
+ // CHECK-FIXES: {{^}} void a() override;
+};
+template <> void MembersOfSpecializations<3>::a() {}
+void ff() { MembersOfSpecializations<3>().a(); };
+
+// In case try statement is used as a method body,
+// make sure that override fix is placed before try keyword.
+struct TryStmtAsBody : public Base {
+ void a() try
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+ // CHECK-FIXES: {{^}} void a() override try
+ { b(); } catch(...) { c(); }
+
+ virtual void d() try
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+ // CHECK-FIXES: {{^}} void d() override try
+ { e(); } catch(...) { f(); }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-transparent-functors %t -- -- -std=c++14
+
+namespace std {
+template<class T>
+struct remove_reference;
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &t);
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &&t);
+
+template <typename T = void>
+struct plus {
+ constexpr T operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct plus<void> {
+ template <typename T, typename U>
+ constexpr auto operator()(T &&Lhs, U &&Rhs) const ->
+ decltype(forward<T>(Lhs) + forward<U>(Rhs));
+};
+
+template <typename T = void>
+struct less {
+ constexpr bool operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct less<void> {
+ template <typename T, typename U>
+ constexpr bool operator()(T &&Lhs, U &&Rhs) const;
+};
+
+template <typename T = void>
+struct logical_not {
+ constexpr bool operator()(const T &Arg) const;
+};
+
+template <>
+struct logical_not<void> {
+ template <typename T>
+ constexpr bool operator()(T &&Arg) const;
+};
+
+template <typename T>
+class allocator;
+
+template <
+ class Key,
+ class Compare = std::less<>,
+ class Allocator = std::allocator<Key>>
+class set {};
+
+template <
+ class Key,
+ class Compare = std::less<Key>,
+ class Allocator = std::allocator<Key>>
+class set2 {};
+
+template <class InputIt, class UnaryPredicate>
+InputIt find_if(InputIt first, InputIt last,
+ UnaryPredicate p);
+
+template <class RandomIt, class Compare>
+void sort(RandomIt first, RandomIt last, Compare comp);
+
+class iterator {};
+class string {};
+}
+
+int main() {
+ using std::set;
+ using std::less;
+ std::set<int, std::less<int>> s;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer transparent functors 'less<>' [modernize-use-transparent-functors]
+ // CHECK-FIXES: {{^}} std::set<int, std::less<>> s;{{$}}
+ set<int, std::less<int>> s2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+ // CHECK-FIXES: {{^}} set<int, std::less<>> s2;{{$}}
+ set<int, less<int>> s3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+ // CHECK-FIXES: {{^}} set<int, less<>> s3;{{$}}
+ std::set<int, std::less<>> s4;
+ std::set<char *, std::less<std::string>> s5;
+ std::set<set<int, less<int>>, std::less<>> s6;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: prefer transparent functors
+ // CHECK-FIXES: {{^}} std::set<set<int, less<>>, std::less<>> s6;{{$}}
+ std::iterator begin, end;
+ sort(begin, end, std::less<int>());
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: prefer transparent functors
+ std::sort(begin, end, std::less<>());
+ find_if(begin, end, std::logical_not<bool>());
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+ std::find_if(begin, end, std::logical_not<>());
+ using my_set = std::set<int, std::less<int>>;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: prefer transparent functors
+ // CHECK-FIXES: {{^}} using my_set = std::set<int, std::less<>>;{{$}}
+ using my_set2 = std::set<char*, std::less<std::string>>;
+ using my_less = std::less<std::string>;
+ find_if(begin, end, my_less());
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+ std::set2<int> control;
+}
+
+
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-using %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-using.IgnoreMacros, value: 0}]}" \
+// RUN: -- -std=c++11
+
+#define CODE typedef int INT
+
+CODE;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define CODE typedef int INT
+// CHECK-FIXES: CODE;
+
+struct Foo;
+#define Bar Baz
+typedef Foo Bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define Bar Baz
+// CHECK-FIXES: using Baz = Foo;
+
+#define TYPEDEF typedef
+TYPEDEF Foo Bak;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define TYPEDEF typedef
+// CHECK-FIXES: TYPEDEF Foo Bak;
--- /dev/null
+// RUN: %check_clang_tidy %s modernize-use-using %t
+
+typedef int Type;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
+// CHECK-FIXES: using Type = int;
+
+typedef long LL;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using LL = long;
+
+typedef int Bla;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bla = int;
+
+typedef Bla Bla2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bla2 = Bla;
+
+typedef void (*type)(int, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using type = void (*)(int, int);
+
+typedef void (*type2)();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using type2 = void (*)();
+
+class Class {
+ typedef long long Type;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+ // CHECK-FIXES: using Type = long long;
+};
+
+typedef void (Class::*MyPtrType)(Bla) const;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using MyPtrType = void (Class::*)(Bla)[[ATTR:( __attribute__\(\(thiscall\)\))?]] const;
+
+class Iterable {
+public:
+ class Iterator {};
+};
+
+template <typename T>
+class Test {
+ typedef typename T::iterator Iter;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+ // CHECK-FIXES: using Iter = typename T::iterator;
+};
+
+using balba = long long;
+
+union A {};
+
+typedef void (A::*PtrType)(int, int) const;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using PtrType = void (A::*)(int, int)[[ATTR]] const;
+
+typedef Class some_class;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using some_class = Class;
+
+typedef Class Cclass;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Cclass = Class;
+
+typedef Cclass cclass2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using cclass2 = Cclass;
+
+class cclass {};
+
+typedef void (cclass::*MyPtrType3)(Bla);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using MyPtrType3 = void (cclass::*)(Bla)[[ATTR]];
+
+using my_class = int;
+
+typedef Test<my_class *> another;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using another = Test<my_class *>;
+
+typedef int* PInt;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using PInt = int *;
+
+typedef int bla1, bla2, bla3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef int bla1, bla2, bla3;
+
+#define CODE typedef int INT
+
+CODE;
+// CHECK-FIXES: #define CODE typedef int INT
+// CHECK-FIXES: CODE;
+
+struct Foo;
+#define Bar Baz
+typedef Foo Bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define Bar Baz
+// CHECK-FIXES: using Baz = Foo;
+
+#define TYPEDEF typedef
+TYPEDEF Foo Bak;
+// CHECK-FIXES: #define TYPEDEF typedef
+// CHECK-FIXES: TYPEDEF Foo Bak;
+
+#define FOO Foo
+typedef FOO Bam;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define FOO Foo
+// CHECK-FIXES: using Bam = Foo;
+
+typedef struct Foo Bap;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap = struct Foo;
+
+struct Foo typedef Bap2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap2 = struct Foo;
+
+Foo typedef Bap3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap3 = Foo;
+
+typedef struct Unknown Baq;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Baq = struct Unknown;
+
+struct Unknown2 typedef Baw;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Baw = struct Unknown2;
+
+int typedef Bax;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bax = int;
+
+typedef struct Q1 { int a; } S1;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef struct Q1 { int a; } S1;
+typedef struct { int b; } S2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef struct { int b; } S2;
+struct Q2 { int c; } typedef S3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: struct Q2 { int c; } typedef S3;
+struct { int d; } typedef S4;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: struct { int d; } typedef S4;
+
+namespace my_space {
+ class my_cclass {};
+ typedef my_cclass FuncType;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using FuncType = my_cclass;
+}
+
+#define lol 4
+typedef unsigned Map[lol];
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef unsigned Map[lol];
+
+typedef void (*fun_type)();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using fun_type = void (*)();
--- /dev/null
+// RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch
+
+#include "mpimock.h"
+
+void negativeTests() {
+ char *buf;
+ MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer [mpi-buffer-deref]
+
+ unsigned **buf2;
+ MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer
+
+ short buf3[1][1];
+ MPI_Send(buf3, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: array->array
+
+ long double _Complex *buf4[1];
+ MPI_Send(buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array
+
+ std::complex<float> *buf5[1][1];
+ MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array->array->pointer
+}
+
+void positiveTests() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+ unsigned *buf2;
+ MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD);
+
+ short buf3[1][1];
+ MPI_Send(buf3[0], 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+
+ long double _Complex *buf4[1];
+ MPI_Send(*buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ long double _Complex buf5[1];
+ MPI_Send(buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ std::complex<float> *buf6[1][1];
+ MPI_Send(*buf6[0], 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ // Referencing an array with '&' is valid, as this also points to the
+ // beginning of the array.
+ long double _Complex buf7[1];
+ MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch
+
+#include "mpimock.h"
+
+void charNegativeTest() {
+ int buf;
+ MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int' does not match the MPI datatype 'MPI_CHAR'
+
+ short buf2;
+ MPI_Send(&buf2, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CHAR'
+
+ long buf3;
+ MPI_Send(&buf3, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_CHAR'
+
+ int8_t buf4;
+ MPI_Send(&buf4, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_CHAR'
+
+ uint16_t buf5;
+ MPI_Send(&buf5, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CHAR'
+
+ long double _Complex buf6;
+ MPI_Send(&buf6, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_CHAR'
+
+ std::complex<float> buf7;
+ MPI_Send(&buf7, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_CHAR'
+}
+
+void intNegativeTest() {
+ unsigned char buf;
+ MPI_Send(&buf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned char' does not match the MPI datatype 'MPI_INT'
+
+ unsigned buf2;
+ MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT'
+
+ short buf3;
+ MPI_Send(&buf3, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT'
+
+ long buf4;
+ MPI_Send(&buf4, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_INT'
+
+ int8_t buf5;
+ MPI_Send(&buf5, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_INT'
+
+ uint16_t buf6;
+ MPI_Send(&buf6, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT'
+
+ long double _Complex buf7;
+ MPI_Send(&buf7, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT'
+
+ std::complex<float> buf8;
+ MPI_Send(&buf8, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT'
+}
+
+void longNegativeTest() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_LONG'
+
+ unsigned buf2;
+ MPI_Send(&buf2, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_LONG'
+
+ unsigned short buf3;
+ MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned short' does not match the MPI datatype 'MPI_LONG'
+
+ unsigned long buf4;
+ MPI_Send(&buf4, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_LONG'
+
+ int8_t buf5;
+ MPI_Send(&buf5, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_LONG'
+
+ uint16_t buf6;
+ MPI_Send(&buf6, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_LONG'
+
+ long double _Complex buf7;
+ MPI_Send(&buf7, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_LONG'
+
+ std::complex<float> buf8;
+ MPI_Send(&buf8, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_LONG'
+}
+
+void int8_tNegativeTest() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_INT8_T'
+
+ unsigned buf2;
+ MPI_Send(&buf2, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT8_T'
+
+ short buf3;
+ MPI_Send(&buf3, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT8_T'
+
+ unsigned long buf4;
+ MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_INT8_T'
+
+ uint8_t buf5;
+ MPI_Send(&buf5, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_INT8_T'
+
+ uint16_t buf6;
+ MPI_Send(&buf6, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT8_T'
+
+ long double _Complex buf7;
+ MPI_Send(&buf7, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT8_T'
+
+ std::complex<float> buf8;
+ MPI_Send(&buf8, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT8_T'
+}
+
+void complex_c_long_double_complexNegativeTest() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ unsigned buf2;
+ MPI_Send(&buf2, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ short buf3;
+ MPI_Send(&buf3, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ unsigned long buf4;
+ MPI_Send(&buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ uint8_t buf5;
+ MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ uint16_t buf6;
+ MPI_Send(&buf6, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ double _Complex buf7;
+ MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+ std::complex<float> buf8;
+ MPI_Send(&buf8, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+}
+
+void complex_cxx_float_complexNegativeTest() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ unsigned buf2;
+ MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ short buf3;
+ MPI_Send(&buf3, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ unsigned long buf4;
+ MPI_Send(&buf4, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ uint8_t buf5;
+ MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ uint16_t buf6;
+ MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ double _Complex buf7;
+ MPI_Send(&buf7, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+ std::complex<double> buf8;
+ MPI_Send(&buf8, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<double>' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+}
+
+void skippedTypesTests() {
+ // typedefs, user defined MPI and nullptr types are skipped
+ typedef char CHAR;
+ CHAR buf;
+ MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ typedef unsigned UNSIGNED;
+ UNSIGNED buf2;
+ MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+#define _MPI_LONG MPI_LONG
+ int buf3;
+ MPI_Send(&buf3, 1, _MPI_LONG, 0, 0, MPI_COMM_WORLD);
+
+#define _MPI_CXX_FLOAT_COMPLEX MPI_CXX_FLOAT_COMPLEX
+ short buf4;
+ MPI_Send(&buf4, 1, _MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ MPI_Send(NULL, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+}
+
+void positiveTests() {
+ char buf;
+ MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+ int buf2;
+ MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+
+ long buf3;
+ MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+
+ int8_t buf4;
+ MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+
+ long double _Complex buf5;
+ MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ std::complex<float> buf6;
+ MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+ uint8_t buf7;
+ MPI_Send(&buf7, 1, MPI_UINT8_T, 0, 0, MPI_COMM_WORLD);
+
+ uint16_t buf8;
+ MPI_Send(&buf8, 1, MPI_UINT16_T, 0, 0, MPI_COMM_WORLD);
+
+ // 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.
+ unsigned char buf9;
+ MPI_Send(&buf9, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult %t -- -extra-arg=-Wunused-variable -- -I%S/Inputs/nolint
+
+#include "trigger_warning.h"
+void I(int& Out) {
+ int In;
+ A1(In, Out);
+}
+// CHECK-MESSAGES-NOT: trigger_warning.h:{{.*}} warning
+// CHECK-MESSAGES-NOT: :[[@LINE-4]]:{{.*}} note
+
+class A { A(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+class B { B(int i); }; // NOLINT
+
+class C { C(int i); }; // NOLINT(we-dont-care-about-categories-yet)
+
+void f() {
+ int i;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'i' [clang-diagnostic-unused-variable]
+ int j; // NOLINT
+}
+
+#define MACRO(X) class X { X(int i); };
+MACRO(D)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit
+MACRO(E) // NOLINT
+
+#define MACRO_NOARG class F { F(int i); };
+MACRO_NOARG // NOLINT
+
+#define MACRO_NOLINT class G { G(int i); }; // NOLINT
+MACRO_NOLINT
+
+#define DOUBLE_MACRO MACRO(H) // NOLINT
+DOUBLE_MACRO
+
+// CHECK-MESSAGES: Suppressed 8 warnings (8 NOLINT)
--- /dev/null
+class A { A(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+// NOLINTNEXTLINE
+class B { B(int i); };
+
+// NOLINTNEXTLINE(we-dont-care-about-categories-yet)
+class C { C(int i); };
+
+
+// NOLINTNEXTLINE
+
+class D { D(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+// NOLINTNEXTLINE
+//
+class E { E(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+#define MACRO(X) class X { X(int i); };
+MACRO(F)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit
+// NOLINTNEXTLINE
+MACRO(G)
+
+#define MACRO_NOARG class H { H(int i); };
+// NOLINTNEXTLINE
+MACRO_NOARG
+
+// CHECK-MESSAGES: Suppressed 4 warnings (4 NOLINT)
+
+// RUN: %check_clang_tidy %s google-explicit-constructor %t --
--- /dev/null
+// RUN: clang-tidy -checks=-*,llvm-include-order -header-filter=.* %s \
+// RUN: -- -isystem %S/Inputs/Headers -I %S/Inputs/overlapping | \
+// RUN: not grep "note: this fix will not be applied because it overlaps with another fix"
+
+#include <s.h>
+#include "o.h"
+
+// Test that clang-tidy takes into account in which file we are doing the
+// replacements to determine if they overlap or not. In the file "o.h" there is
+// a similar error at the same file offset, but they do not overlap.
--- /dev/null
+// RUN: %check_clang_tidy %s performance-faster-string-find %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: performance-faster-string-find.StringLikeClasses, \
+// RUN: value: 'std::basic_string; ::llvm::StringRef;'}]}" --
+
+namespace std {
+template <typename Char>
+struct basic_string {
+ int find(const Char *, int = 0) const;
+ int find(const Char *, int, int) const;
+ int rfind(const Char *) const;
+ int find_first_of(const Char *) const;
+ int find_first_not_of(const Char *) const;
+ int find_last_of(const Char *) const;
+ int find_last_not_of(const Char *) const;
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+} // namespace std
+
+namespace llvm {
+struct StringRef {
+ int find(const char *) const;
+};
+} // namespace llvm
+
+struct NotStringRef {
+ int find(const char *);
+};
+
+void StringFind() {
+ std::string Str;
+
+ Str.find("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'find' called with a string literal consisting of a single character; consider using the more effective overload accepting a character [performance-faster-string-find]
+ // CHECK-FIXES: Str.find('a');
+
+ // Works with the pos argument.
+ Str.find("a", 1);
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'find' called with a string literal
+ // CHECK-FIXES: Str.find('a', 1);
+
+ // Doens't work with strings smaller or larger than 1 char.
+ Str.find("");
+ Str.find("ab");
+
+ // Doesn't do anything with the 3 argument overload.
+ Str.find("a", 1, 1);
+
+ // Other methods that can also be replaced
+ Str.rfind("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'rfind' called with a string literal
+ // CHECK-FIXES: Str.rfind('a');
+ Str.find_first_of("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'find_first_of' called with a string
+ // CHECK-FIXES: Str.find_first_of('a');
+ Str.find_first_not_of("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'find_first_not_of' called with a
+ // CHECK-FIXES: Str.find_first_not_of('a');
+ Str.find_last_of("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'find_last_of' called with a string
+ // CHECK-FIXES: Str.find_last_of('a');
+ Str.find_last_not_of("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'find_last_not_of' called with a
+ // CHECK-FIXES: Str.find_last_not_of('a');
+
+ // std::wstring should work.
+ std::wstring WStr;
+ WStr.find(L"n");
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'find' called with a string literal
+ // CHECK-FIXES: Str.find(L'n');
+ // Even with unicode that fits in one wide char.
+ WStr.find(L"\x3A9");
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'find' called with a string literal
+ // CHECK-FIXES: Str.find(L'\x3A9');
+
+ // Also with other types, but only if it was specified in the options.
+ llvm::StringRef sr;
+ sr.find("x");
+ // CHECK-MESSAGES: [[@LINE-1]]:11: warning: 'find' called with a string literal
+ // CHECK-FIXES: sr.find('x');
+ NotStringRef nsr;
+ nsr.find("x");
+}
+
+
+template <typename T>
+int FindTemplateDependant(T value) {
+ return value.find("A");
+}
+template <typename T>
+int FindTemplateNotDependant(T pos) {
+ return std::string().find("A", pos);
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: 'find' called with a string literal
+ // CHECK-FIXES: return std::string().find('A', pos);
+}
+
+int FindStr() {
+ return FindTemplateDependant(std::string()) + FindTemplateNotDependant(1);
+}
+
+#define STR_MACRO(str) str.find("A")
+#define POS_MACRO(pos) std::string().find("A",pos)
+
+int Macros() {
+ return STR_MACRO(std::string()) + POS_MACRO(1);
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'find' called with a string literal
+ // CHECK-MESSAGES: [[@LINE-2]]:37: warning: 'find' called with a string literal
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-for-range-copy %t -config="{CheckOptions: [{key: "performance-for-range-copy.WarnOnAllAutoCopies", value: 1}]}" -- -std=c++11
+
+template <typename T>
+struct Iterator {
+ void operator++() {}
+ const T& operator*() {
+ static T* TT = new T();
+ return *TT;
+ }
+ bool operator!=(const Iterator &) { return false; }
+};
+template <typename T>
+struct View {
+ T begin() { return T(); }
+ T begin() const { return T(); }
+ T end() { return T(); }
+ T end() const { return T(); }
+};
+
+struct S {
+ S();
+ S(const S &);
+ ~S();
+ S &operator=(const S &);
+};
+
+void NegativeLoopVariableNotAuto() {
+ for (S S1 : View<Iterator<S>>()) {
+ S* S2 = &S1;
+ }
+}
+
+void PositiveTriggeredForAutoLoopVariable() {
+ for (auto S1 : View<Iterator<S>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& S1 : View<Iterator<S>>()) {
+ S* S2 = &S1;
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-for-range-copy %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+namespace std {
+
+template <typename _Tp>
+struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) {
+ return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
+}
+
+} // std
+
+template <typename T>
+struct Iterator {
+ void operator++() {}
+ const T& operator*() {
+ static T* TT = new T();
+ return *TT;
+ }
+ bool operator!=(const Iterator &) { return false; }
+ typedef const T& const_reference;
+};
+template <typename T>
+struct View {
+ T begin() { return T(); }
+ T begin() const { return T(); }
+ T end() { return T(); }
+ T end() const { return T(); }
+ typedef typename T::const_reference const_reference;
+};
+
+struct ConstructorConvertible {
+};
+
+struct S {
+ S();
+ S(const S &);
+ S(const ConstructorConvertible&) {}
+ ~S();
+ S &operator=(const S &);
+};
+
+struct Convertible {
+ operator S() const {
+ return S();
+ }
+};
+
+void negativeConstReference() {
+ for (const S &S1 : View<Iterator<S>>()) {
+ }
+}
+
+void negativeUserDefinedConversion() {
+ Convertible C[0];
+ for (const S &S1 : C) {
+ }
+}
+
+void negativeImplicitConstructorConversion() {
+ ConstructorConvertible C[0];
+ for (const S &S1 : C) {
+ }
+}
+
+template <typename T>
+void uninstantiated() {
+ for (const S S1 : View<Iterator<S>>()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
+ // CHECK-FIXES: {{^}} for (const S& S1 : View<Iterator<S>>()) {}
+
+ // Don't warn on dependent types.
+ for (const T t1 : View<Iterator<T>>()) {
+ }
+}
+
+template <typename T>
+void instantiated() {
+ for (const S S2 : View<Iterator<S>>()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
+ // CHECK-FIXES: {{^}} for (const S& S2 : View<Iterator<S>>()) {}
+
+ for (const T T2 : View<Iterator<T>>()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
+ // CHECK-FIXES: {{^}} for (const T& T2 : View<Iterator<T>>()) {}
+}
+
+template <typename T>
+void instantiatedNegativeTypedefConstReference() {
+ for (typename T::const_reference T2 : T()) {
+ S S1 = T2;
+ }
+}
+
+void f() {
+ instantiated<int>();
+ instantiated<S>();
+ instantiatedNegativeTypedefConstReference<View<Iterator<S>>>();
+}
+
+struct Mutable {
+ Mutable() {}
+ Mutable(const Mutable &) = default;
+ Mutable(const Mutable &, const Mutable &) {}
+ void setBool(bool B) {}
+ bool constMethod() const {
+ return true;
+ }
+ Mutable& operator[](int I) {
+ return *this;
+ }
+ bool operator==(const Mutable &Other) const {
+ return true;
+ }
+ ~Mutable() {}
+};
+
+Mutable& operator<<(Mutable &Out, bool B) {
+ Out.setBool(B);
+ return Out;
+}
+
+bool operator!=(const Mutable& M1, const Mutable& M2) {
+ return false;
+}
+
+void use(const Mutable &M);
+void use(int I);
+void useTwice(const Mutable &M1, const Mutable &M2);
+void useByValue(Mutable M);
+void useByConstValue(const Mutable M);
+void mutate(Mutable *M);
+void mutate(Mutable &M);
+void onceConstOnceMutated(const Mutable &M1, Mutable &M2);
+
+void negativeVariableIsMutated() {
+ for (auto M : View<Iterator<Mutable>>()) {
+ mutate(M);
+ }
+ for (auto M : View<Iterator<Mutable>>()) {
+ mutate(&M);
+ }
+ for (auto M : View<Iterator<Mutable>>()) {
+ M.setBool(true);
+ }
+}
+
+void negativeOnceConstOnceMutated() {
+ for (auto M : View<Iterator<Mutable>>()) {
+ onceConstOnceMutated(M, M);
+ }
+}
+
+void negativeVarIsMoved() {
+ for (auto M : View<Iterator<Mutable>>()) {
+ auto Moved = std::move(M);
+ }
+}
+
+void negativeNonConstOperatorIsInvoked() {
+ for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ auto& N = NonConstOperatorInvokee[0];
+ }
+}
+
+void negativeNonConstNonMemberOperatorInvoked() {
+ for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ NonConstOperatorInvokee << true;
+ }
+}
+
+void negativeConstCheapToCopy() {
+ for (const int I : View<Iterator<int>>()) {
+ }
+}
+
+void negativeConstCheapToCopyTypedef() {
+ typedef const int ConstInt;
+ for (ConstInt C : View<Iterator<ConstInt>>()) {
+ }
+}
+
+void negativeCheapToCopy() {
+ for (int I : View<Iterator<int>>()) {
+ use(I);
+ }
+}
+
+void negativeCheapToCopyTypedef() {
+ typedef int Int;
+ for (Int I : View<Iterator<Int>>()) {
+ use(I);
+ }
+}
+
+void positiveOnlyConstMethodInvoked() {
+ for (auto M : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& M : View<Iterator<Mutable>>()) {
+ M.constMethod();
+ }
+}
+
+void positiveOnlyUsedAsConstArguments() {
+ for (auto UsedAsConst : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& UsedAsConst : View<Iterator<Mutable>>()) {
+ use(UsedAsConst);
+ useTwice(UsedAsConst, UsedAsConst);
+ useByValue(UsedAsConst);
+ useByConstValue(UsedAsConst);
+ }
+}
+
+void positiveOnlyUsedInCopyConstructor() {
+ for (auto A : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
+ Mutable Copy = A;
+ Mutable Copy2(A);
+ }
+}
+
+void positiveTwoConstConstructorArgs() {
+ for (auto A : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
+ Mutable Copy(A, A);
+ }
+}
+
+void PositiveConstMemberOperatorInvoked() {
+ for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ bool result = ConstOperatorInvokee == Mutable();
+ }
+}
+
+void PositiveConstNonMemberOperatorInvoked() {
+ for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+ // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+ bool result = ConstOperatorInvokee != Mutable();
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-implicit-cast-in-loop %t
+
+// ---------- Classes used in the tests ----------
+
+// Iterator returning by value.
+template <typename T>
+struct Iterator {
+ void operator++();
+ T operator*();
+ bool operator!=(const Iterator& other);
+};
+
+// Iterator returning by reference.
+template <typename T>
+struct RefIterator {
+ void operator++();
+ T& operator*();
+ bool operator!=(const RefIterator& other);
+};
+
+// The template argument is an iterator type, and a view is an object you can
+// run a for loop on.
+template <typename T>
+struct View {
+ T begin();
+ T end();
+};
+
+// With this class, the implicit cast is a call to the (implicit) constructor of
+// the class.
+template <typename T>
+class ImplicitWrapper {
+ public:
+ // Implicit!
+ ImplicitWrapper(const T& t);
+};
+
+// With this class, the implicit cast is a call to the conversion operators of
+// SimpleClass and ComplexClass.
+template <typename T>
+class OperatorWrapper {
+ public:
+ explicit OperatorWrapper(const T& t);
+};
+
+struct SimpleClass {
+ int foo;
+ operator OperatorWrapper<SimpleClass>();
+};
+
+// The materialize expression is not the same when the class has a destructor,
+// so we make sure we cover that case too.
+class ComplexClass {
+ public:
+ ComplexClass();
+ ~ComplexClass();
+ operator OperatorWrapper<ComplexClass>();
+};
+
+typedef View<Iterator<SimpleClass>> SimpleView;
+typedef View<RefIterator<SimpleClass>> SimpleRefView;
+typedef View<Iterator<ComplexClass>> ComplexView;
+typedef View<RefIterator<ComplexClass>> ComplexRefView;
+
+// ---------- The test themselves ----------
+// For each test we do, in the same order, const ref, non const ref, const
+// value, non const value.
+
+void SimpleClassIterator() {
+ for (const SimpleClass& foo : SimpleView()) {}
+ // This line does not compile because a temporary cannot be assigned to a non
+ // const reference.
+ // for (SimpleClass& foo : SimpleView()) {}
+ for (const SimpleClass foo : SimpleView()) {}
+ for (SimpleClass foo : SimpleView()) {}
+}
+
+void SimpleClassRefIterator() {
+ for (const SimpleClass& foo : SimpleRefView()) {}
+ for (SimpleClass& foo : SimpleRefView()) {}
+ for (const SimpleClass foo : SimpleRefView()) {}
+ for (SimpleClass foo : SimpleRefView()) {}
+}
+
+void ComplexClassIterator() {
+ for (const ComplexClass& foo : ComplexView()) {}
+ // for (ComplexClass& foo : ComplexView()) {}
+ for (const ComplexClass foo : ComplexView()) {}
+ for (ComplexClass foo : ComplexView()) {}
+}
+
+void ComplexClassRefIterator() {
+ for (const ComplexClass& foo : ComplexRefView()) {}
+ for (ComplexClass& foo : ComplexRefView()) {}
+ for (const ComplexClass foo : ComplexRefView()) {}
+ for (ComplexClass foo : ComplexRefView()) {}
+}
+
+void ImplicitSimpleClassIterator() {
+ for (const ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the loop variable 'foo' is different from the one returned by the iterator and generates an implicit cast; you can either change the type to the correct one ('const SimpleClass &' but 'const auto&' is always a valid option) or remove the reference to make it explicit that you are creating a new value [performance-implicit-cast-in-loop]
+ // for (ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
+ for (const ImplicitWrapper<SimpleClass> foo : SimpleView()) {}
+ for (ImplicitWrapper<SimpleClass>foo : SimpleView()) {}
+}
+
+void ImplicitSimpleClassRefIterator() {
+ for (const ImplicitWrapper<SimpleClass>& foo : SimpleRefView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+ // for (ImplicitWrapper<SimpleClass>& foo : SimpleRefView()) {}
+ for (const ImplicitWrapper<SimpleClass> foo : SimpleRefView()) {}
+ for (ImplicitWrapper<SimpleClass>foo : SimpleRefView()) {}
+}
+
+void ImplicitComplexClassIterator() {
+ for (const ImplicitWrapper<ComplexClass>& foo : ComplexView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+ // for (ImplicitWrapper<ComplexClass>& foo : ComplexView()) {}
+ for (const ImplicitWrapper<ComplexClass> foo : ComplexView()) {}
+ for (ImplicitWrapper<ComplexClass>foo : ComplexView()) {}
+}
+
+void ImplicitComplexClassRefIterator() {
+ for (const ImplicitWrapper<ComplexClass>& foo : ComplexRefView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+ // for (ImplicitWrapper<ComplexClass>& foo : ComplexRefView()) {}
+ for (const ImplicitWrapper<ComplexClass> foo : ComplexRefView()) {}
+ for (ImplicitWrapper<ComplexClass>foo : ComplexRefView()) {}
+}
+
+void OperatorSimpleClassIterator() {
+ for (const OperatorWrapper<SimpleClass>& foo : SimpleView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+ // for (OperatorWrapper<SimpleClass>& foo : SimpleView()) {}
+ for (const OperatorWrapper<SimpleClass> foo : SimpleView()) {}
+ for (OperatorWrapper<SimpleClass>foo : SimpleView()) {}
+}
+
+void OperatorSimpleClassRefIterator() {
+ for (const OperatorWrapper<SimpleClass>& foo : SimpleRefView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+ // for (OperatorWrapper<SimpleClass>& foo : SimpleRefView()) {}
+ for (const OperatorWrapper<SimpleClass> foo : SimpleRefView()) {}
+ for (OperatorWrapper<SimpleClass>foo : SimpleRefView()) {}
+}
+
+void OperatorComplexClassIterator() {
+ for (const OperatorWrapper<ComplexClass>& foo : ComplexView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+ // for (OperatorWrapper<ComplexClass>& foo : ComplexView()) {}
+ for (const OperatorWrapper<ComplexClass> foo : ComplexView()) {}
+ for (OperatorWrapper<ComplexClass>foo : ComplexView()) {}
+}
+
+void OperatorComplexClassRefIterator() {
+ for (const OperatorWrapper<ComplexClass>& foo : ComplexRefView()) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+ // for (OperatorWrapper<ComplexClass>& foo : ComplexRefView()) {}
+ for (const OperatorWrapper<ComplexClass> foo : ComplexRefView()) {}
+ for (OperatorWrapper<ComplexClass>foo : ComplexRefView()) {}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-inefficient-string-concatenation %t
+
+namespace std {
+template <typename T>
+class basic_string {
+public:
+ basic_string() {}
+ ~basic_string() {}
+ basic_string<T> *operator+=(const basic_string<T> &) {}
+ friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &) {}
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f(std::string) {}
+std::string g(std::string) {}
+
+int main() {
+ std::string mystr1, mystr2;
+ std::wstring mywstr1, mywstr2;
+
+ for (int i = 0; i < 10; ++i) {
+ f(mystr1 + mystr2 + mystr1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: string concatenation results in allocation of unnecessary temporary strings; consider using 'operator+=' or 'string::append()' instead
+ mystr1 = mystr1 + mystr2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+ mystr1 = mystr2 + mystr2 + mystr2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: string concatenation
+ mystr1 = mystr2 + mystr1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+ mywstr1 = mywstr2 + mywstr1;
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+ mywstr1 = mywstr2 + mywstr2 + mywstr2;
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: string concatenation
+
+ mywstr1 = mywstr2 + mywstr2;
+ mystr1 = mystr2 + mystr2;
+ mystr1 += mystr2;
+ f(mystr2 + mystr1);
+ mystr1 = g(mystr1);
+ }
+ return 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm -- --std=c++11
+
+namespace std {
+
+typedef int size_t;
+
+template<class E> class initializer_list {
+public:
+ using value_type = E;
+ using reference = E&;
+ using const_reference = const E&;
+ using size_type = size_t;
+ using iterator = const E*;
+ using const_iterator = const E*;
+ initializer_list();
+ size_t size() const; // number of elements
+ const E* begin() const; // first element
+ const E* end() const; // one past the last element
+};
+
+// initializer list range access
+template<class E> const E* begin(initializer_list<E> il);
+template<class E> const E* end(initializer_list<E> il);
+
+template <class T>
+class vector {
+ public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+
+ explicit vector();
+ explicit vector(size_type n);
+
+ void push_back(const T& val);
+
+ template <class... Args> void emplace_back(Args &&... args);
+
+ void reserve(size_t n);
+ void resize(size_t n);
+
+ size_t size();
+ const_reference operator[] (size_type) const;
+ reference operator[] (size_type);
+
+ const_iterator begin() const;
+ const_iterator end() const;
+};
+} // namespace std
+
+class Foo {
+ public:
+ explicit Foo(int);
+};
+
+class Bar {
+ public:
+ Bar(int);
+};
+
+int Op(int);
+
+void f(std::vector<int>& t) {
+ {
+ std::vector<int> v0;
+ // CHECK-FIXES: v0.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ v0.push_back(i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the vector capacity before the loop
+ }
+ {
+ std::vector<int> v1;
+ // CHECK-FIXES: v1.reserve(10);
+ for (int i = 0; i < 10; i++)
+ v1.push_back(i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ {
+ std::vector<int> v2;
+ // CHECK-FIXES: v2.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ v2.push_back(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ {
+ std::vector<int> v3;
+ // CHECK-FIXES: v3.reserve(5);
+ for (int i = 0; i < 5; ++i) {
+ v3.push_back(i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ // CHECK-FIXES-NOT: v3.reserve(10);
+ for (int i = 0; i < 10; ++i) {
+ // No fix for this loop as we encounter the prior loops.
+ v3.push_back(i);
+ }
+ }
+ {
+ std::vector<int> v4;
+ std::vector<int> v5;
+ v5.reserve(3);
+ // CHECK-FIXES: v4.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ v4.push_back(i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ {
+ std::vector<int> v6;
+ // CHECK-FIXES: v6.reserve(t.size());
+ for (std::size_t i = 0; i < t.size(); ++i) {
+ v6.push_back(t[i]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<int> v7;
+ // CHECK-FIXES: v7.reserve(t.size() - 1);
+ for (std::size_t i = 0; i < t.size() - 1; ++i) {
+ v7.push_back(t[i]);
+ } // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ {
+ std::vector<int> v8;
+ // CHECK-FIXES: v8.reserve(t.size());
+ for (const auto &e : t) {
+ v8.push_back(e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<int> v9;
+ // CHECK-FIXES: v9.reserve(t.size());
+ for (const auto &e : t) {
+ v9.push_back(Op(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<Foo> v10;
+ // CHECK-FIXES: v10.reserve(t.size());
+ for (const auto &e : t) {
+ v10.push_back(Foo(e));
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<Bar> v11;
+ // CHECK-FIXES: v11.reserve(t.size());
+ for (const auto &e : t) {
+ v11.push_back(e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+ }
+ }
+ {
+ std::vector<Foo> v12;
+ // CHECK-FIXES: v12.reserve(t.size());
+ for (const auto &e : t) {
+ v12.emplace_back(e);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called
+ }
+ }
+
+ // ---- Non-fixed Cases ----
+ {
+ std::vector<int> z0;
+ z0.reserve(20);
+ // CHECK-FIXES-NOT: z0.reserve(10);
+ // There is a "reserve" call already.
+ for (int i = 0; i < 10; ++i) {
+ z0.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z1;
+ z1.reserve(5);
+ // CHECK-FIXES-NOT: z1.reserve(10);
+ // There is a "reserve" call already.
+ for (int i = 0; i < 10; ++i) {
+ z1.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z2;
+ z2.resize(5);
+ // CHECK-FIXES-NOT: z2.reserve(10);
+ // There is a ref usage of v before the loop.
+ for (int i = 0; i < 10; ++i) {
+ z2.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z3;
+ z3.push_back(0);
+ // CHECK-FIXES-NOT: z3.reserve(10);
+ // There is a ref usage of v before the loop.
+ for (int i = 0; i < 10; ++i) {
+ z3.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z4;
+ f(z4);
+ // CHECK-FIXES-NOT: z4.reserve(10);
+ // There is a ref usage of z4 before the loop.
+ for (int i = 0; i < 10; ++i) {
+ z4.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z5(20);
+ // CHECK-FIXES-NOT: z5.reserve(10);
+ // z5 is not constructed with default constructor.
+ for (int i = 0; i < 10; ++i) {
+ z5.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z6;
+ // CHECK-FIXES-NOT: z6.reserve(10);
+ // For-loop is not started with 0.
+ for (int i = 1; i < 10; ++i) {
+ z6.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z7;
+ // CHECK-FIXES-NOT: z7.reserve(t.size());
+ // z7 isn't referenced in for-loop body.
+ for (std::size_t i = 0; i < t.size(); ++i) {
+ t.push_back(i);
+ }
+ }
+ {
+ std::vector<int> z8;
+ int k;
+ // CHECK-FIXES-NOT: z8.reserve(10);
+ // For-loop isn't a fixable loop.
+ for (std::size_t i = 0; k < 10; ++i) {
+ z8.push_back(t[i]);
+ }
+ }
+ {
+ std::vector<int> z9;
+ // CHECK-FIXES-NOT: z9.reserve(i + 1);
+ // The loop end expression refers to the loop variable i.
+ for (int i = 0; i < i + 1; i++)
+ z9.push_back(i);
+ }
+ {
+ std::vector<int> z10;
+ int k;
+ // CHECK-FIXES-NOT: z10.reserve(10);
+ // For-loop isn't a fixable loop.
+ for (std::size_t i = 0; i < 10; ++k) {
+ z10.push_back(t[i]);
+ }
+ }
+ {
+ std::vector<int> z11;
+ // initializer_list should not trigger the check.
+ for (int e : {1, 2, 3, 4, 5}) {
+ z11.push_back(e);
+ }
+ }
+ {
+ std::vector<int> z12;
+ std::vector<int>* z13 = &t;
+ // We only support detecting the range init expression which references
+ // container directly.
+ // Complex range init expressions like `*z13` is not supported.
+ for (const auto &e : *z13) {
+ z12.push_back(e);
+ }
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-type-promotion-in-math-fn %t
+
+// CHECK-FIXES: #include <cmath>
+
+double acos(double);
+double acosh(double);
+double asin(double);
+double asinh(double);
+double atan2(double, double);
+double atan(double);
+double atanh(double);
+double cbrt(double);
+double ceil(double);
+double copysign(double, double);
+double cos(double);
+double cosh(double);
+double erfc(double);
+double erf(double);
+double exp2(double);
+double exp(double);
+double expm1(double);
+double fabs(double);
+double fdim(double, double);
+double floor(double);
+double fma(double, double, double);
+double fmax(double, double);
+double fmin(double, double);
+double fmod(double, double);
+double frexp(double, int *);
+double hypot(double, double);
+double ilogb(double);
+double ldexp(double, double);
+double lgamma(double);
+long long llrint(double);
+double log10(double);
+double log1p(double);
+double log2(double);
+double logb(double);
+double log(double);
+long lrint(double);
+double modf(double);
+double nearbyint(double);
+double nextafter(double, double);
+double nexttoward(double, long double);
+double pow(double, double);
+double remainder(double, double);
+double remquo(double, double, int *);
+double rint(double);
+double round(double);
+double scalbln(double, long);
+double scalbn(double, int);
+double sin(double);
+double sinh(double);
+double sqrt(double);
+double tan(double);
+double tanh(double);
+double tgamma(double);
+double trunc(double);
+long long llround(double);
+long lround(double);
+
+void check_all_fns() {
+ float a, b, c;
+ int i;
+ long l;
+ int *int_ptr;
+
+ acos(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acos' promotes float to double [performance-type-promotion-in-math-fn]
+ // CHECK-FIXES: {{^}} std::acos(a);{{$}}
+ acosh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acosh'
+ // CHECK-FIXES: {{^}} std::acosh(a);{{$}}
+ asin(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asin'
+ // CHECK-FIXES: {{^}} std::asin(a);{{$}}
+ asinh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asinh'
+ // CHECK-FIXES: {{^}} std::asinh(a);{{$}}
+ atan2(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan2'
+ // CHECK-FIXES: {{^}} std::atan2(a, b);{{$}}
+ atan(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan'
+ // CHECK-FIXES: {{^}} std::atan(a);{{$}}
+ atanh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atanh'
+ // CHECK-FIXES: {{^}} std::atanh(a);{{$}}
+ cbrt(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cbrt'
+ // CHECK-FIXES: {{^}} std::cbrt(a);{{$}}
+ ceil(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ceil'
+ // CHECK-FIXES: {{^}} std::ceil(a);{{$}}
+ copysign(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'copysign'
+ // CHECK-FIXES: {{^}} std::copysign(a, b);{{$}}
+ cos(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cos'
+ // CHECK-FIXES: {{^}} std::cos(a);{{$}}
+ cosh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cosh'
+ // CHECK-FIXES: {{^}} std::cosh(a);{{$}}
+ erf(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erf'
+ // CHECK-FIXES: {{^}} std::erf(a);{{$}}
+ erfc(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erfc'
+ // CHECK-FIXES: {{^}} std::erfc(a);{{$}}
+ exp2(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp2'
+ // CHECK-FIXES: {{^}} std::exp2(a);{{$}}
+ exp(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp'
+ // CHECK-FIXES: {{^}} std::exp(a);{{$}}
+ expm1(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'expm1'
+ // CHECK-FIXES: {{^}} std::expm1(a);{{$}}
+ fabs(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fabs'
+ // CHECK-FIXES: {{^}} std::fabs(a);{{$}}
+ fdim(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fdim'
+ // CHECK-FIXES: {{^}} std::fdim(a, b);{{$}}
+ floor(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'floor'
+ // CHECK-FIXES: {{^}} std::floor(a);{{$}}
+ fma(a, b, c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fma'
+ // CHECK-FIXES: {{^}} std::fma(a, b, c);{{$}}
+ fmax(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmax'
+ // CHECK-FIXES: {{^}} std::fmax(a, b);{{$}}
+ fmin(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmin'
+ // CHECK-FIXES: {{^}} std::fmin(a, b);{{$}}
+ fmod(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmod'
+ // CHECK-FIXES: {{^}} std::fmod(a, b);{{$}}
+ frexp(a, int_ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'frexp'
+ // CHECK-FIXES: {{^}} std::frexp(a, int_ptr);{{$}}
+ hypot(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'hypot'
+ // CHECK-FIXES: {{^}} std::hypot(a, b);{{$}}
+ ilogb(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ilogb'
+ // CHECK-FIXES: {{^}} std::ilogb(a);{{$}}
+ ldexp(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ldexp'
+ // CHECK-FIXES: {{^}} std::ldexp(a, b);{{$}}
+ lgamma(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lgamma'
+ // CHECK-FIXES: {{^}} std::lgamma(a);{{$}}
+ llrint(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llrint'
+ // CHECK-FIXES: {{^}} std::llrint(a);{{$}}
+ llround(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llround'
+ // CHECK-FIXES: {{^}} std::llround(a);{{$}}
+ log10(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log10'
+ // CHECK-FIXES: {{^}} std::log10(a);{{$}}
+ log1p(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log1p'
+ // CHECK-FIXES: {{^}} std::log1p(a);{{$}}
+ log2(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log2'
+ // CHECK-FIXES: {{^}} std::log2(a);{{$}}
+ log(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log'
+ // CHECK-FIXES: {{^}} std::log(a);{{$}}
+ logb(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'logb'
+ // CHECK-FIXES: {{^}} std::logb(a);{{$}}
+ lrint(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lrint'
+ // CHECK-FIXES: {{^}} std::lrint(a);{{$}}
+ lround(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lround'
+ // CHECK-FIXES: {{^}} std::lround(a);{{$}}
+ nearbyint(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nearbyint'
+ // CHECK-FIXES: {{^}} std::nearbyint(a);{{$}}
+ nextafter(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nextafter'
+ // CHECK-FIXES: {{^}} std::nextafter(a, b);{{$}}
+ nexttoward(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+ // CHECK-FIXES: {{^}} std::nexttoward(a, b);{{$}}
+ pow(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'pow'
+ // CHECK-FIXES: {{^}} std::pow(a, b);{{$}}
+ remainder(a, b);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remainder'
+ // CHECK-FIXES: {{^}} std::remainder(a, b);{{$}}
+ remquo(a, b, int_ptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remquo'
+ // CHECK-FIXES: {{^}} std::remquo(a, b, int_ptr);{{$}}
+ rint(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'rint'
+ // CHECK-FIXES: {{^}} std::rint(a);{{$}}
+ round(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'round'
+ // CHECK-FIXES: {{^}} std::round(a);{{$}}
+ scalbln(a, l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+ // CHECK-FIXES: {{^}} std::scalbln(a, l);{{$}}
+ scalbn(a, i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+ // CHECK-FIXES: {{^}} std::scalbn(a, i);{{$}}
+ sin(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin'
+ // CHECK-FIXES: {{^}} std::sin(a);{{$}}
+ sinh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sinh'
+ // CHECK-FIXES: {{^}} std::sinh(a);{{$}}
+ sqrt(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sqrt'
+ // CHECK-FIXES: {{^}} std::sqrt(a);{{$}}
+ tan(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tan'
+ // CHECK-FIXES: {{^}} std::tan(a);{{$}}
+ tanh(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tanh'
+ // CHECK-FIXES: {{^}} std::tanh(a);{{$}}
+ tgamma(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tgamma'
+ // CHECK-FIXES: {{^}} std::tgamma(a);{{$}}
+ trunc(a);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'trunc'
+ // CHECK-FIXES: {{^}} std::trunc(a);{{$}}
+}
+
+// nexttoward/nexttowardf are weird -- the second param is always long double.
+// So we warn if the first arg is a float, regardless of what the second arg is.
+void check_nexttoward() {
+ nexttoward(0.f, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+ // CHECK-FIXES: {{^}} std::nexttoward(0.f, 0);{{$}}
+ nexttoward(0.f, 0l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+ // CHECK-FIXES: {{^}} std::nexttoward(0.f, 0l);{{$}}
+ nexttoward(0.f, 0.f);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+ // CHECK-FIXES: {{^}} std::nexttoward(0.f, 0.f);{{$}}
+ nexttoward(0.f, 0.);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+ // CHECK-FIXES: {{^}} std::nexttoward(0.f, 0.);{{$}}
+
+ // No warnings for these.
+ nexttoward(0., 0);
+ nexttoward(0., 0.f);
+ nexttoward(0., 0.);
+}
+
+// The second parameter to scalbn and scalbnf is an int, so we don't care what
+// type you pass as that argument; we warn iff the first argument is a float.
+void check_scalbn() {
+ scalbn(0.f, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+ // CHECK-FIXES: {{^}} std::scalbn(0.f, 0);{{$}}
+ scalbn(0.f, static_cast<char>(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+ // CHECK-FIXES: {{^}} std::scalbn(0.f, static_cast<char>(0));{{$}}
+
+ // No warnings for these.
+ scalbn(0., 0);
+ scalbn(0., static_cast<char>(0));
+}
+
+// scalbln/scalblnf are like scalbn/scalbnf except their second arg is a long.
+// Again, doesn't matter what we pass for the second arg; we warn iff the first
+// arg is a float.
+void check_scalbln() {
+ scalbln(0.f, 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+ // CHECK-FIXES: {{^}} std::scalbln(0.f, 0);{{$}}
+ scalbln(0.f, 0l);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+ // CHECK-FIXES: {{^}} std::scalbln(0.f, 0l);{{$}}
+
+ // No warnings for these.
+ scalbln(0., 0);
+ scalbln(0., 0l);
+}
+
+float cosf(float);
+double foo(double); // not a math.h function
+float cos(float); // not a math.h function (wrong signature)
+double cos(double, double); // not a math.h function (wrong signature)
+
+namespace std {
+void cos(float);
+} // namespace std
+
+void check_no_warnings() {
+ foo(0.); // no warning because not a math.h function.
+
+ sin(0); // no warning because arg is an int
+ cos(0.); // no warning because arg is a double
+ std::cos(0.f); // no warning because not ::cos.
+ cosf(0.f); // no warning; we expect this to take a float
+ cos(0.f); // does not match the expected signature of ::cos
+ cos(0.f, 0.f); // does not match the expected signature of ::cos
+
+ // No warnings because all args are not floats.
+ remainder(0., 0.f);
+ remainder(0.f, 0.);
+ remainder(0, 0.f);
+ remainder(0.f, 0);
+ fma(0.f, 0.f, 0);
+ fma(0.f, 0.f, 0.);
+ fma(0.f, 0., 0.f);
+ fma(0., 0.f, 0.f);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-unnecessary-copy-initialization %t
+
+struct ExpensiveToCopyType {
+ ExpensiveToCopyType();
+ virtual ~ExpensiveToCopyType();
+ const ExpensiveToCopyType &reference() const;
+ void nonConstMethod();
+ bool constMethod() const;
+};
+
+struct TrivialToCopyType {
+ const TrivialToCopyType &reference() const;
+};
+
+struct WeirdCopyCtorType {
+ WeirdCopyCtorType();
+ WeirdCopyCtorType(const WeirdCopyCtorType &w, bool oh_yes = true);
+
+ void nonConstMethod();
+ bool constMethod() const;
+};
+
+ExpensiveToCopyType global_expensive_to_copy_type;
+
+const ExpensiveToCopyType &ExpensiveTypeReference();
+const TrivialToCopyType &TrivialTypeReference();
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstPointer(const ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+void PositiveFunctionCall() {
+ const auto AutoAssigned = ExpensiveTypeReference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned' is copy-constructed from a const reference; consider making it a const reference [performance-unnecessary-copy-initialization]
+ // CHECK-FIXES: const auto& AutoAssigned = ExpensiveTypeReference();
+ const auto AutoCopyConstructed(ExpensiveTypeReference());
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+ // CHECK-FIXES: const auto& AutoCopyConstructed(ExpensiveTypeReference());
+ const ExpensiveToCopyType VarAssigned = ExpensiveTypeReference();
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = ExpensiveTypeReference();
+ const ExpensiveToCopyType VarCopyConstructed(ExpensiveTypeReference());
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(ExpensiveTypeReference());
+}
+
+void PositiveMethodCallConstReferenceParam(const ExpensiveToCopyType &Obj) {
+ const auto AutoAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+ const auto AutoCopyConstructed(Obj.reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+ // CHECK-FIXES: const auto& AutoCopyConstructed(Obj.reference());
+ const ExpensiveToCopyType VarAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj.reference();
+ const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj.reference());
+}
+
+void PositiveMethodCallConstParam(const ExpensiveToCopyType Obj) {
+ const auto AutoAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+ const auto AutoCopyConstructed(Obj.reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+ // CHECK-FIXES: const auto& AutoCopyConstructed(Obj.reference());
+ const ExpensiveToCopyType VarAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj.reference();
+ const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj.reference());
+}
+
+void PositiveMethodCallConstPointerParam(const ExpensiveToCopyType *const Obj) {
+ const auto AutoAssigned = Obj->reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj->reference();
+ const auto AutoCopyConstructed(Obj->reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+ // CHECK-FIXES: const auto& AutoCopyConstructed(Obj->reference());
+ const ExpensiveToCopyType VarAssigned = Obj->reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj->reference();
+ const ExpensiveToCopyType VarCopyConstructed(Obj->reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj->reference());
+}
+
+void PositiveLocalConstValue() {
+ const ExpensiveToCopyType Obj;
+ const auto UnnecessaryCopy = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+ // CHECK-FIXES: const auto& UnnecessaryCopy = Obj.reference();
+}
+
+void PositiveLocalConstRef() {
+ const ExpensiveToCopyType Obj;
+ const ExpensiveToCopyType &ConstReference = Obj.reference();
+ const auto UnnecessaryCopy = ConstReference.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+ // CHECK-FIXES: const auto& UnnecessaryCopy = ConstReference.reference();
+}
+
+void PositiveLocalConstPointer() {
+ const ExpensiveToCopyType Obj;
+ const ExpensiveToCopyType *const ConstPointer = &Obj;
+ const auto UnnecessaryCopy = ConstPointer->reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+ // CHECK-FIXES: const auto& UnnecessaryCopy = ConstPointer->reference();
+}
+
+void NegativeFunctionCallTrivialType() {
+ const auto AutoAssigned = TrivialTypeReference();
+ const auto AutoCopyConstructed(TrivialTypeReference());
+ const TrivialToCopyType VarAssigned = TrivialTypeReference();
+ const TrivialToCopyType VarCopyConstructed(TrivialTypeReference());
+}
+
+void NegativeStaticLocalVar(const ExpensiveToCopyType &Obj) {
+ static const auto StaticVar = Obj.reference();
+}
+
+void PositiveFunctionCallExpensiveTypeNonConstVariable() {
+ auto AutoAssigned = ExpensiveTypeReference();
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: the variable 'AutoAssigned' is copy-constructed from a const reference but is only used as const reference; consider making it a const reference [performance-unnecessary-copy-initialization]
+ // CHECK-FIXES: const auto& AutoAssigned = ExpensiveTypeReference();
+ auto AutoCopyConstructed(ExpensiveTypeReference());
+ // CHECK-MESSAGES: [[@LINE-1]]:8: warning: the variable 'AutoCopyConstructed'
+ // CHECK-FIXES: const auto& AutoCopyConstructed(ExpensiveTypeReference());
+ ExpensiveToCopyType VarAssigned = ExpensiveTypeReference();
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: the variable 'VarAssigned'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = ExpensiveTypeReference();
+ ExpensiveToCopyType VarCopyConstructed(ExpensiveTypeReference());
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: the variable 'VarCopyConstructed'
+ // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(ExpensiveTypeReference());
+}
+
+void positiveNonConstVarInCodeBlock(const ExpensiveToCopyType &Obj) {
+ {
+ auto Assigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: the variable 'Assigned'
+ // CHECK-FIXES: const auto& Assigned = Obj.reference();
+ Assigned.reference();
+ useAsConstReference(Assigned);
+ useByValue(Assigned);
+ }
+}
+
+void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType &Obj) {
+ {
+ auto NonConstInvoked = Obj.reference();
+ // CHECK-FIXES: auto NonConstInvoked = Obj.reference();
+ NonConstInvoked.nonConstMethod();
+ }
+ {
+ auto Reassigned = Obj.reference();
+ // CHECK-FIXES: auto Reassigned = Obj.reference();
+ Reassigned = ExpensiveToCopyType();
+ }
+ {
+ auto MutatedByReference = Obj.reference();
+ // CHECK-FIXES: auto MutatedByReference = Obj.reference();
+ mutate(MutatedByReference);
+ }
+ {
+ auto MutatedByPointer = Obj.reference();
+ // CHECK-FIXES: auto MutatedByPointer = Obj.reference();
+ mutate(&MutatedByPointer);
+ }
+}
+
+void PositiveMethodCallNonConstRefNotModified(ExpensiveToCopyType &Obj) {
+ const auto AutoAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+}
+
+void NegativeMethodCallNonConstRefIsModified(ExpensiveToCopyType &Obj) {
+ const auto AutoAssigned = Obj.reference();
+ const auto AutoCopyConstructed(Obj.reference());
+ const ExpensiveToCopyType VarAssigned = Obj.reference();
+ const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+ mutate(&Obj);
+}
+
+void PositiveMethodCallNonConstNotModified(ExpensiveToCopyType Obj) {
+ const auto AutoAssigned = Obj.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+}
+
+void NegativeMethodCallNonConstValueArgumentIsModified(ExpensiveToCopyType Obj) {
+ Obj.nonConstMethod();
+ const auto AutoAssigned = Obj.reference();
+}
+
+void PositiveMethodCallNonConstPointerNotModified(ExpensiveToCopyType *const Obj) {
+ const auto AutoAssigned = Obj->reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = Obj->reference();
+ Obj->constMethod();
+}
+
+void NegativeMethodCallNonConstPointerIsModified(ExpensiveToCopyType *const Obj) {
+ const auto AutoAssigned = Obj->reference();
+ const auto AutoCopyConstructed(Obj->reference());
+ const ExpensiveToCopyType VarAssigned = Obj->reference();
+ const ExpensiveToCopyType VarCopyConstructed(Obj->reference());
+ mutate(Obj);
+}
+
+void PositiveLocalVarIsNotModified() {
+ ExpensiveToCopyType LocalVar;
+ const auto AutoAssigned = LocalVar.reference();
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+ // CHECK-FIXES: const auto& AutoAssigned = LocalVar.reference();
+}
+
+void NegativeLocalVarIsModified() {
+ ExpensiveToCopyType Obj;
+ const auto AutoAssigned = Obj.reference();
+ Obj = AutoAssigned;
+}
+
+struct NegativeConstructor {
+ NegativeConstructor(const ExpensiveToCopyType &Obj) : Obj(Obj) {}
+ ExpensiveToCopyType Obj;
+};
+
+#define UNNECESSARY_COPY_INIT_IN_MACRO_BODY(TYPE) \
+ void functionWith##TYPE(const TYPE &T) { \
+ auto AssignedInMacro = T.reference(); \
+ } \
+// Ensure fix is not applied.
+// CHECK-FIXES: auto AssignedInMacro = T.reference();
+
+UNNECESSARY_COPY_INIT_IN_MACRO_BODY(ExpensiveToCopyType)
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the variable 'AssignedInMacro' is copy-constructed
+
+#define UNNECESSARY_COPY_INIT_IN_MACRO_ARGUMENT(ARGUMENT) ARGUMENT
+
+void PositiveMacroArgument(const ExpensiveToCopyType &Obj) {
+ UNNECESSARY_COPY_INIT_IN_MACRO_ARGUMENT(auto CopyInMacroArg = Obj.reference());
+ // CHECK-MESSAGES: [[@LINE-1]]:48: warning: the variable 'CopyInMacroArg' is copy-constructed
+ // Ensure fix is not applied.
+ // CHECK-FIXES: auto CopyInMacroArg = Obj.reference()
+}
+
+void PositiveLocalCopyConstMethodInvoked() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType copy_1 = orig;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_1' of the variable 'orig' is never modified; consider avoiding the copy [performance-unnecessary-copy-initialization]
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_1 = orig;
+ copy_1.constMethod();
+ orig.constMethod();
+}
+
+void PositiveLocalCopyUsingExplicitCopyCtor() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType copy_2(orig);
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_2'
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_2(orig);
+ copy_2.constMethod();
+ orig.constMethod();
+}
+
+void PositiveLocalCopyCopyIsArgument(const ExpensiveToCopyType &orig) {
+ ExpensiveToCopyType copy_3 = orig;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_3'
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_3 = orig;
+ copy_3.constMethod();
+}
+
+void PositiveLocalCopyUsedAsConstRef() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType copy_4 = orig;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_4'
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_4 = orig;
+ useAsConstReference(orig);
+}
+
+void PositiveLocalCopyTwice() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType copy_5 = orig;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_5'
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_5 = orig;
+ ExpensiveToCopyType copy_6 = copy_5;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_6'
+ // CHECK-FIXES: const ExpensiveToCopyType& copy_6 = copy_5;
+ copy_5.constMethod();
+ copy_6.constMethod();
+ orig.constMethod();
+}
+
+
+void PositiveLocalCopyWeirdCopy() {
+ WeirdCopyCtorType orig;
+ WeirdCopyCtorType weird_1(orig);
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: local copy 'weird_1'
+ // CHECK-FIXES: const WeirdCopyCtorType& weird_1(orig);
+ weird_1.constMethod();
+
+ WeirdCopyCtorType weird_2 = orig;
+ // CHECK-MESSAGES: [[@LINE-1]]:21: warning: local copy 'weird_2'
+ // CHECK-FIXES: const WeirdCopyCtorType& weird_2 = orig;
+ weird_2.constMethod();
+}
+
+void NegativeLocalCopySimpleTypes() {
+ int i1 = 0;
+ int i2 = i1;
+}
+
+void NegativeLocalCopyCopyIsModified() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType neg_copy_1 = orig;
+ neg_copy_1.nonConstMethod();
+}
+
+void NegativeLocalCopyOriginalIsModified() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType neg_copy_2 = orig;
+ orig.nonConstMethod();
+}
+
+void NegativeLocalCopyUsedAsRefArg() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType neg_copy_3 = orig;
+ mutate(neg_copy_3);
+}
+
+void NegativeLocalCopyUsedAsPointerArg() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType neg_copy_4 = orig;
+ mutate(&neg_copy_4);
+}
+
+void NegativeLocalCopyCopyFromGlobal() {
+ ExpensiveToCopyType neg_copy_5 = global_expensive_to_copy_type;
+}
+
+void NegativeLocalCopyCopyToStatic() {
+ ExpensiveToCopyType orig;
+ static ExpensiveToCopyType neg_copy_6 = orig;
+}
+
+void NegativeLocalCopyNonConstInForLoop() {
+ ExpensiveToCopyType orig;
+ for (ExpensiveToCopyType neg_copy_7 = orig; orig.constMethod();
+ orig.nonConstMethod()) {
+ orig.constMethod();
+ }
+}
+
+void NegativeLocalCopyWeirdNonCopy() {
+ WeirdCopyCtorType orig;
+ WeirdCopyCtorType neg_weird_1(orig, false);
+ WeirdCopyCtorType neg_weird_2(orig, true);
+}
+void WarningOnlyMultiDeclStmt() {
+ ExpensiveToCopyType orig;
+ ExpensiveToCopyType copy = orig, copy2;
+ // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy' of the variable 'orig' is never modified; consider avoiding the copy [performance-unnecessary-copy-initialization]
+ // CHECK-FIXES: ExpensiveToCopyType copy = orig, copy2;
+}
+
+class Element {};
+class Container {
+public:
+ class Iterator {
+ public:
+ void operator++();
+ Element operator*();
+ bool operator!=(const Iterator &);
+ WeirdCopyCtorType c;
+ };
+ const Iterator &begin() const;
+ const Iterator &end() const;
+};
+
+void implicitVarFalsePositive() {
+ for (const Element &E : Container()) {
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t -- -- -fdelayed-template-parsing
+
+struct ExpensiveToCopyType {
+ const ExpensiveToCopyType & constReference() const {
+ return *this;
+ }
+ void nonConstMethod();
+ virtual ~ExpensiveToCopyType();
+};
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+// This class simulates std::pair<>. It is trivially copy constructible
+// and trivially destructible, but not trivially copy assignable.
+class SomewhatTrivial {
+ public:
+ SomewhatTrivial();
+ SomewhatTrivial(const SomewhatTrivial&) = default;
+ ~SomewhatTrivial() = default;
+ SomewhatTrivial& operator=(const SomewhatTrivial&);
+};
+
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj) {
+ // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'Obj' is copied for each invocation; consider making it a reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj) {
+}
+
+void positiveExpensiveValue(ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveValue(ExpensiveToCopyType Obj) {
+ // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'Obj' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj) {
+ Obj.constReference();
+ useAsConstReference(Obj);
+ auto Copy = Obj;
+ useByValue(Obj);
+}
+
+void positiveWithComment(const ExpensiveToCopyType /* important */ S);
+// CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
+void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
+ // CHECK-MESSAGES: [[@LINE-1]]:68: warning: the const qualified
+ // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S) {
+}
+
+void positiveUnnamedParam(const ExpensiveToCopyType) {
+ // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter #1
+ // CHECK-FIXES: void positiveUnnamedParam(const ExpensiveToCopyType&) {
+}
+
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy);
+// CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy);
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy) {
+ // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter 'ConstCopy'
+ // CHECK-MESSAGES: [[@LINE-2]]:120: warning: the parameter 'Copy'
+ // CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy) {
+}
+
+struct PositiveConstValueConstructor {
+ PositiveConstValueConstructor(const ExpensiveToCopyType ConstCopy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:59: warning: the const qualified parameter 'ConstCopy'
+ // CHECK-FIXES: PositiveConstValueConstructor(const ExpensiveToCopyType& ConstCopy) {}
+};
+
+template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType S, T V) {
+ // CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'S'
+ // CHECK-FIXES: template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType& S, T V) {
+}
+
+void instantiated() {
+ templateWithNonTemplatizedParameter(ExpensiveToCopyType(), ExpensiveToCopyType());
+ templateWithNonTemplatizedParameter(ExpensiveToCopyType(), 5);
+}
+
+template <typename T> void negativeTemplateType(const T V) {
+}
+
+void negativeArray(const ExpensiveToCopyType[]) {
+}
+
+void negativePointer(ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstPointer(const ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstReference(const ExpensiveToCopyType& Obj) {
+}
+
+void negativeReference(ExpensiveToCopyType& Obj) {
+}
+
+void negativeUniversalReference(ExpensiveToCopyType&& Obj) {
+}
+
+void negativeSomewhatTrivialConstValue(const SomewhatTrivial Somewhat) {
+}
+
+void negativeSomewhatTrivialValue(SomewhatTrivial Somewhat) {
+}
+
+void negativeConstBuiltIn(const int I) {
+}
+
+void negativeValueBuiltIn(int I) {
+}
+
+void negativeValueIsMutatedByReference(ExpensiveToCopyType Obj) {
+ mutate(Obj);
+}
+
+void negativeValueIsMutatatedByPointer(ExpensiveToCopyType Obj) {
+ mutate(&Obj);
+}
+
+void negativeValueIsReassigned(ExpensiveToCopyType Obj) {
+ Obj = ExpensiveToCopyType();
+}
+
+void negativeValueNonConstMethodIsCalled(ExpensiveToCopyType Obj) {
+ Obj.nonConstMethod();
+}
+
+struct PositiveValueUnusedConstructor {
+ PositiveValueUnusedConstructor(ExpensiveToCopyType Copy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+ // CHECK-FIXES: PositiveValueUnusedConstructor(const ExpensiveToCopyType& Copy) {}
+};
+
+struct PositiveValueCopiedConstructor {
+ PositiveValueCopiedConstructor(ExpensiveToCopyType Copy) : Field(Copy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+ // CHECK-FIXES: PositiveValueCopiedConstructor(const ExpensiveToCopyType& Copy) : Field(Copy) {}
+ ExpensiveToCopyType Field;
+};
+
+template <typename T>
+struct Container {
+ typedef const T & const_reference;
+};
+
+void NegativeTypedefParam(const Container<ExpensiveToCopyType>::const_reference Param) {
+}
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY() \
+ void inMacro(const ExpensiveToCopyType T) { \
+ } \
+// Ensure fix is not applied.
+// CHECK-FIXES: void inMacro(const ExpensiveToCopyType T) {
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the const qualified parameter 'T'
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(ARGUMENT) \
+ ARGUMENT
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(void inMacroArgument(const ExpensiveToCopyType InMacroArg) {})
+// CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'InMacroArg'
+// CHECK-FIXES: void inMacroArgument(const ExpensiveToCopyType InMacroArg) {}
+
+struct VirtualMethod {
+ virtual ~VirtualMethod() {}
+ virtual void handle(ExpensiveToCopyType T) const = 0;
+};
+
+struct NegativeOverriddenMethod : public VirtualMethod {
+ void handle(ExpensiveToCopyType Overridden) const {
+ // CHECK-FIXES: handle(ExpensiveToCopyType Overridden) const {
+ }
+};
+
+struct NegativeDeletedMethod {
+ ~NegativeDeletedMethod() {}
+ NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+ // CHECK-FIXES: NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+};
--- /dev/null
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: cp %S/Inputs/performance-unnecessary-value-param/header.h %t/header.h
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t/temp -- -- -std=c++11 -I %t
+// RUN: diff %t/header.h %S/Inputs/performance-unnecessary-value-param/header-fixed.h
+
+#include "header.h"
+
+
+
+int f1(int n, ABC v1, ABC v2) {
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the parameter 'v1' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-MESSAGES: [[@LINE-2]]:27: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: int f1(int n, const ABC& v1, const ABC& v2) {
+ return v1.get(n) + v2.get(n);
+}
+int f2(int n, ABC v2) {
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: int f2(int n, const ABC& v2) {
+}
--- /dev/null
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t -- -fix-errors -- --std=c++11
+
+// Ensure that incomplete types result in an error from the frontend and not a
+// clang-tidy diagnostic about IncompleteType being expensive to copy.
+struct IncompleteType;
+void NegativeForIncompleteType(IncompleteType I) {
+ // CHECK-MESSAGES: [[@LINE-1]]:47: error: variable has incomplete type 'IncompleteType' [clang-diagnostic-error]
+}
+
--- /dev/null
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t
+
+// CHECK-FIXES: #include <utility>
+
+struct ExpensiveToCopyType {
+ const ExpensiveToCopyType & constReference() const {
+ return *this;
+ }
+ void nonConstMethod();
+ virtual ~ExpensiveToCopyType();
+};
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+// This class simulates std::pair<>. It is trivially copy constructible
+// and trivially destructible, but not trivially copy assignable.
+class SomewhatTrivial {
+ public:
+ SomewhatTrivial();
+ SomewhatTrivial(const SomewhatTrivial&) = default;
+ ~SomewhatTrivial() = default;
+ SomewhatTrivial& operator=(const SomewhatTrivial&);
+};
+
+struct MoveOnlyType {
+ MoveOnlyType(const MoveOnlyType &) = delete;
+ MoveOnlyType(MoveOnlyType &&) = default;
+ ~MoveOnlyType();
+ void constMethod() const;
+};
+
+struct ExpensiveMovableType {
+ ExpensiveMovableType();
+ ExpensiveMovableType(ExpensiveMovableType &&);
+ ExpensiveMovableType(const ExpensiveMovableType &) = default;
+ ExpensiveMovableType &operator=(const ExpensiveMovableType &) = default;
+ ExpensiveMovableType &operator=(ExpensiveMovableType &&);
+ ~ExpensiveMovableType();
+};
+
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj) {
+ // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'Obj' is copied for each invocation; consider making it a reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj) {
+}
+
+void positiveExpensiveValue(ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveValue(ExpensiveToCopyType Obj) {
+ // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'Obj' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+ // CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj) {
+ Obj.constReference();
+ useAsConstReference(Obj);
+ auto Copy = Obj;
+ useByValue(Obj);
+}
+
+void positiveWithComment(const ExpensiveToCopyType /* important */ S);
+// CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
+void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
+ // CHECK-MESSAGES: [[@LINE-1]]:68: warning: the const qualified
+ // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S) {
+}
+
+void positiveUnnamedParam(const ExpensiveToCopyType) {
+ // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter #1
+ // CHECK-FIXES: void positiveUnnamedParam(const ExpensiveToCopyType&) {
+}
+
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy);
+// CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy);
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy) {
+ // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter 'ConstCopy'
+ // CHECK-MESSAGES: [[@LINE-2]]:120: warning: the parameter 'Copy'
+ // CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy) {
+}
+
+struct PositiveConstValueConstructor {
+ PositiveConstValueConstructor(const ExpensiveToCopyType ConstCopy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:59: warning: the const qualified parameter 'ConstCopy'
+ // CHECK-FIXES: PositiveConstValueConstructor(const ExpensiveToCopyType& ConstCopy) {}
+};
+
+template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType S, T V) {
+ // CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'S'
+ // CHECK-FIXES: template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType& S, T V) {
+}
+
+void instantiated() {
+ templateWithNonTemplatizedParameter(ExpensiveToCopyType(), ExpensiveToCopyType());
+ templateWithNonTemplatizedParameter(ExpensiveToCopyType(), 5);
+}
+
+template <typename T> void negativeTemplateType(const T V) {
+}
+
+void negativeArray(const ExpensiveToCopyType[]) {
+}
+
+void negativePointer(ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstPointer(const ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstReference(const ExpensiveToCopyType& Obj) {
+}
+
+void negativeReference(ExpensiveToCopyType& Obj) {
+}
+
+void negativeUniversalReference(ExpensiveToCopyType&& Obj) {
+}
+
+void negativeSomewhatTrivialConstValue(const SomewhatTrivial Somewhat) {
+}
+
+void negativeSomewhatTrivialValue(SomewhatTrivial Somewhat) {
+}
+
+void negativeConstBuiltIn(const int I) {
+}
+
+void negativeValueBuiltIn(int I) {
+}
+
+void negativeValueIsMutatedByReference(ExpensiveToCopyType Obj) {
+ mutate(Obj);
+}
+
+void negativeValueIsMutatatedByPointer(ExpensiveToCopyType Obj) {
+ mutate(&Obj);
+}
+
+void negativeValueIsReassigned(ExpensiveToCopyType Obj) {
+ Obj = ExpensiveToCopyType();
+}
+
+void negativeValueNonConstMethodIsCalled(ExpensiveToCopyType Obj) {
+ Obj.nonConstMethod();
+}
+
+struct PositiveValueUnusedConstructor {
+ PositiveValueUnusedConstructor(ExpensiveToCopyType Copy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+ // CHECK-FIXES: PositiveValueUnusedConstructor(const ExpensiveToCopyType& Copy) {}
+};
+
+struct PositiveValueCopiedConstructor {
+ PositiveValueCopiedConstructor(ExpensiveToCopyType Copy) : Field(Copy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+ // CHECK-FIXES: PositiveValueCopiedConstructor(const ExpensiveToCopyType& Copy) : Field(Copy) {}
+ ExpensiveToCopyType Field;
+};
+
+struct PositiveValueMovableConstructor {
+ PositiveValueMovableConstructor(ExpensiveMovableType Copy) : Field(Copy) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:70: warning: parameter 'Copy'
+ // CHECK-FIXES: PositiveValueMovableConstructor(ExpensiveMovableType Copy) : Field(std::move(Copy)) {}
+ ExpensiveMovableType Field;
+};
+
+struct NegativeValueMovedConstructor {
+ NegativeValueMovedConstructor(ExpensiveMovableType Copy) : Field(static_cast<ExpensiveMovableType &&>(Copy)) {}
+ ExpensiveMovableType Field;
+};
+
+template <typename T>
+struct Container {
+ typedef const T & const_reference;
+};
+
+void NegativeTypedefParam(const Container<ExpensiveToCopyType>::const_reference Param) {
+}
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY() \
+ void inMacro(const ExpensiveToCopyType T) { \
+ } \
+// Ensure fix is not applied.
+// CHECK-FIXES: void inMacro(const ExpensiveToCopyType T) {
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the const qualified parameter 'T'
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(ARGUMENT) \
+ ARGUMENT
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(void inMacroArgument(const ExpensiveToCopyType InMacroArg) {})
+// CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'InMacroArg'
+// CHECK-FIXES: void inMacroArgument(const ExpensiveToCopyType InMacroArg) {}
+
+struct VirtualMethod {
+ virtual ~VirtualMethod() {}
+ virtual void handle(ExpensiveToCopyType T) const = 0;
+};
+
+struct NegativeOverriddenMethod : public VirtualMethod {
+ void handle(ExpensiveToCopyType Overridden) const {
+ // CHECK-FIXES: handle(ExpensiveToCopyType Overridden) const {
+ }
+};
+
+struct VirtualMethodWarningOnly {
+ virtual void methodWithExpensiveValueParam(ExpensiveToCopyType T) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:66: warning: the parameter 'T' is copied
+ // CHECK-FIXES: virtual void methodWithExpensiveValueParam(ExpensiveToCopyType T) {}
+ virtual ~VirtualMethodWarningOnly() {}
+};
+
+struct PositiveNonVirualMethod {
+ void method(const ExpensiveToCopyType T) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:41: warning: the const qualified parameter 'T' is copied
+ // CHECK-FIXES: void method(const ExpensiveToCopyType& T) {}
+};
+
+struct NegativeDeletedMethod {
+ ~NegativeDeletedMethod() {}
+ NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+ // CHECK-FIXES: NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+};
+
+void NegativeMoveOnlyTypePassedByValue(MoveOnlyType M) {
+ M.constMethod();
+}
+
+void PositiveMoveOnCopyConstruction(ExpensiveMovableType E) {
+ auto F = E;
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: parameter 'E' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]
+ // CHECK-FIXES: auto F = std::move(E);
+}
+
+void PositiveConstRefNotMoveSinceReferencedMultipleTimes(ExpensiveMovableType E) {
+ // CHECK-MESSAGES: [[@LINE-1]]:79: warning: the parameter 'E' is copied
+ // CHECK-FIXES: void PositiveConstRefNotMoveSinceReferencedMultipleTimes(const ExpensiveMovableType& E) {
+ auto F = E;
+ auto G = E;
+}
+
+void PositiveMoveOnCopyAssignment(ExpensiveMovableType E) {
+ ExpensiveMovableType F;
+ F = E;
+ // CHECK-MESSAGES: [[@LINE-1]]:7: warning: parameter 'E' is passed by value
+ // CHECK-FIXES: F = std::move(E);
+}
+
+struct NotCopyAssigned {
+ NotCopyAssigned &operator=(const ExpensiveMovableType &);
+};
+
+void PositiveNoMoveForNonCopyAssigmentOperator(ExpensiveMovableType E) {
+ // CHECK-MESSAGES: [[@LINE-1]]:69: warning: the parameter 'E' is copied
+ // CHECK-FIXES: void PositiveNoMoveForNonCopyAssigmentOperator(const ExpensiveMovableType& E) {
+ NotCopyAssigned N;
+ N = E;
+}
+
+// The argument could be moved but is not since copy statement is inside a loop.
+void PositiveNoMoveInsideLoop(ExpensiveMovableType E) {
+ // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the parameter 'E' is copied
+ // CHECK-FIXES: void PositiveNoMoveInsideLoop(const ExpensiveMovableType& E) {
+ for (;;) {
+ auto F = E;
+ }
+}
+
+void PositiveConstRefNotMoveConstructible(ExpensiveToCopyType T) {
+ // CHECK-MESSAGES: [[@LINE-1]]:63: warning: the parameter 'T' is copied
+ // CHECK-FIXES: void PositiveConstRefNotMoveConstructible(const ExpensiveToCopyType& T) {
+ auto U = T;
+}
+
+void PositiveConstRefNotMoveAssignable(ExpensiveToCopyType A) {
+ // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the parameter 'A' is copied
+ // CHECK-FIXES: void PositiveConstRefNotMoveAssignable(const ExpensiveToCopyType& A) {
+ ExpensiveToCopyType B;
+ B = A;
+}
+
+// Case where parameter in declaration is already const-qualified but not in
+// implementation. Make sure a second 'const' is not added to the declaration.
+void PositiveConstDeclaration(const ExpensiveToCopyType A);
+// CHECK-FIXES: void PositiveConstDeclaration(const ExpensiveToCopyType& A);
+void PositiveConstDeclaration(ExpensiveToCopyType A) {
+ // CHECK-MESSAGES: [[@LINE-1]]:51: warning: the parameter 'A' is copied
+ // CHECK-FIXES: void PositiveConstDeclaration(const ExpensiveToCopyType& A) {
+}
+
+void PositiveNonConstDeclaration(ExpensiveToCopyType A);
+// CHECK-FIXES: void PositiveNonConstDeclaration(const ExpensiveToCopyType& A);
+void PositiveNonConstDeclaration(const ExpensiveToCopyType A) {
+ // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'A'
+ // CHECK-FIXES: void PositiveNonConstDeclaration(const ExpensiveToCopyType& A) {
+}
+
+void PositiveOnlyMessageAsReferencedInCompilationUnit(ExpensiveToCopyType A) {
+ // CHECK-MESSAGES: [[@LINE-1]]:75: warning: the parameter 'A' is copied
+ // CHECK-FIXES: void PositiveOnlyMessageAsReferencedInCompilationUnit(ExpensiveToCopyType A) {
+}
+
+void ReferenceFunctionOutsideOfCallExpr() {
+ void (*ptr)(ExpensiveToCopyType) = &PositiveOnlyMessageAsReferencedInCompilationUnit;
+}
+
+void PositiveMessageAndFixAsFunctionIsCalled(ExpensiveToCopyType A) {
+ // CHECK-MESSAGES: [[@LINE-1]]:66: warning: the parameter 'A' is copied
+ // CHECK-FIXES: void PositiveMessageAndFixAsFunctionIsCalled(const ExpensiveToCopyType& A) {
+}
+
+void ReferenceFunctionByCallingIt() {
+ PositiveMessageAndFixAsFunctionIsCalled(ExpensiveToCopyType());
+}
+
+// Virtual method overrides of dependent types cannot be recognized unless they
+// are marked as override or final. Test that check is not triggered on methods
+// marked with override or final.
+template <typename T>
+struct NegativeDependentTypeInterface {
+ virtual void Method(ExpensiveToCopyType E) = 0;
+};
+
+template <typename T>
+struct NegativeOverrideImpl : public NegativeDependentTypeInterface<T> {
+ void Method(ExpensiveToCopyType E) override {}
+};
+
+template <typename T>
+struct NegativeFinalImpl : public NegativeDependentTypeInterface<T> {
+ void Method(ExpensiveToCopyType E) final {}
+};
+
+struct PositiveConstructor {
+ PositiveConstructor(ExpensiveToCopyType E) : E(E) {}
+ // CHECK-MESSAGES: [[@LINE-1]]:43: warning: the parameter 'E' is copied
+ // CHECK-FIXES: PositiveConstructor(const ExpensiveToCopyType& E) : E(E) {}
+
+ ExpensiveToCopyType E;
+};
+
+struct NegativeUsingConstructor : public PositiveConstructor {
+ using PositiveConstructor::PositiveConstructor;
+};
+
+void fun() {
+ ExpensiveToCopyType E;
+ NegativeUsingConstructor S(E);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-avoid-const-params-in-decls %t
+
+using alias_type = bool;
+using alias_const_type = const bool;
+
+
+void F1(const int i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls]
+// CHECK-FIXES: void F1(int i);
+
+void F2(const int *const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F2(const int *i);
+
+void F3(int const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F3(int i);
+
+void F4(alias_type const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F4(alias_type i);
+
+void F5(const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-FIXES: void F5(int);
+
+void F6(const int *const);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-FIXES: void F6(const int *);
+
+void F7(int, const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: parameter 2 is const-qualified
+// CHECK-FIXES: void F7(int, int);
+
+void F8(const int, const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-MESSAGES: :[[@LINE-2]]:20: warning: parameter 2 is const-qualified
+// CHECK-FIXES: void F8(int, int);
+
+void F9(const int long_name);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'long_name'
+// CHECK-FIXES: void F9(int long_name);
+
+void F10(const int *const *const long_name);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 'long_name'
+// CHECK-FIXES: void F10(const int *const *long_name);
+
+void F11(const unsigned int /*v*/);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 1
+// CHECK-FIXES: void F11(unsigned int /*v*/);
+
+void F12(const bool b = true);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 'b'
+// CHECK-FIXES: void F12(bool b = true);
+
+template<class T>
+int F13(const bool b = true);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'b'
+// CHECK-FIXES: int F13(bool b = true);
+int f13 = F13<int>();
+
+struct Foo {
+ Foo(const int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: parameter 'i'
+ // CHECK-FIXES: Foo(int i);
+
+ void operator()(const int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
+ // CHECK-FIXES: void operator()(int i);
+};
+
+template <class T>
+struct FooT {
+ FooT(const int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: parameter 'i'
+ // CHECK-FIXES: FooT(int i);
+
+ void operator()(const int i);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
+ // CHECK-FIXES: void operator()(int i);
+};
+FooT<int> f(1);
+
+// Do not match on definitions
+void NF1(const int i) {}
+void NF2(const int *const i) {}
+void NF3(int const i) {}
+void NF4(alias_type const i) {}
+void NF5(const int) {}
+void NF6(const int *const) {}
+void NF7(int, const int) {}
+void NF8(const int, const int) {}
+template <class T>
+int NF9(const int, const int) { return 0; }
+int nf9 = NF9<int>(1, 2);
+
+// Do not match on inline member functions
+struct Bar {
+ Bar(const int i) {}
+
+ void operator()(const int i) {}
+};
+
+// Do not match on inline member functions of a templated class
+template <class T>
+struct BarT {
+ BarT(const int i) {}
+
+ void operator()(const int i) {}
+};
+BarT<int> b(1);
+
+// Do not match on other stuff
+void NF(const alias_type& i);
+void NF(const int &i);
+void NF(const int *i);
+void NF(alias_const_type i);
+void NF(const alias_type&);
+void NF(const int&);
+void NF(const int*);
+void NF(const int* const*);
+void NF(alias_const_type);
+
+// Regression test for when the 'const' token is not in the code.
+#define CONCAT(a, b) a##b
+void ConstNotVisible(CONCAT(cons, t) int i);
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: parameter 'i'
+// We warn, but we can't give a fix
+// CHECK-FIXES: void ConstNotVisible(CONCAT(cons, t) int i);
+
+// Regression test. We should not warn (or crash) on lambda expressions
+auto lambda_with_name = [](const int n) {};
+auto lambda_without_name = [](const int) {};
--- /dev/null
+// RUN: clang-tidy -checks='-*,readability-braces-around-statements' %s --
+
+// Note: this test expects no assert failure happened in clang-tidy.
+
+int test_failure() {
+ if (std::rand()) {
+ }
+}
+
+void test_failure2() {
+ for (a b c;;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 4}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+ return false;
+}
+
+void test() {
+ if (cond("if1") /*comment*/) do_something("same-line");
+
+ if (cond("if2"))
+ do_something("single-line");
+
+ if (cond("if3") /*comment*/)
+ // some comment
+ do_something("three"
+ "lines");
+
+ if (cond("if4") /*comment*/)
+ // some comment
+ do_something("many"
+ "many"
+ "many"
+ "many"
+ "lines");
+ // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if4") /*comment*/) {
+ // CHECK-FIXES: }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -format-style="{IndentWidth: 3}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+ return false;
+}
+
+void test() {
+ if (cond("if0") /*comment*/) do_something("same-line");
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: {{^}} if (cond("if0") /*comment*/) {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} do_something("same-line");{{$}}
+ // CHECK-FIXES-NEXT: {{^}} }{{$}}
+
+ if (1) while (2) if (3) for (;;) do ; while(false) /**/;/**/
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-4]]:35: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-5]]:38: warning: statement should be inside braces
+ // CHECK-FIXES: {{^}} if (1) {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} while (2) {
+ // CHECK-FIXES-NEXT: {{^}} if (3) {
+ // CHECK-FIXES-NEXT: {{^}} for (;;) {
+ // CHECK-FIXES-NEXT: {{^}} do {
+ // CHECK-FIXES-NEXT: {{^}} ;
+ // CHECK-FIXES-NEXT: {{^}} } while (false) /**/; /**/
+ // CHECK-FIXES-NEXT: {{^}} }
+ // CHECK-FIXES-NEXT: {{^}} }
+ // CHECK-FIXES-NEXT: {{^}} }
+ // CHECK-FIXES-NEXT: {{^}} }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 1}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+ return false;
+}
+
+void test() {
+ if (cond("if1") /*comment*/) do_something("same-line");
+
+ if (cond("if2"))
+ do_something("single-line");
+ // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if2")) {
+ // CHECK-FIXES: }
+
+ if (cond("if3") /*comment*/)
+ // some comment
+ do_something("three"
+ "lines");
+ // CHECK-MESSAGES: :[[@LINE-4]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if3") /*comment*/) {
+ // CHECK-FIXES: }
+
+ if (cond("if4") /*comment*/)
+ // some comment
+ do_something("many"
+ "many"
+ "many"
+ "many"
+ "lines");
+ // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if4") /*comment*/) {
+ // CHECK-FIXES: }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 2}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+ return false;
+}
+
+void test() {
+ if (cond("if1") /*comment*/) do_something("same-line");
+
+ if (cond("if2"))
+ do_something("single-line");
+
+ if (cond("if3") /*comment*/)
+ // some comment
+ do_something("three"
+ "lines");
+ // CHECK-MESSAGES: :[[@LINE-4]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if3") /*comment*/) {
+ // CHECK-FIXES: }
+
+ if (cond("if4") /*comment*/)
+ // some comment
+ do_something("many"
+ "many"
+ "many"
+ "many"
+ "lines");
+ // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if4") /*comment*/) {
+ // CHECK-FIXES: }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+ return false;
+}
+
+#define EMPTY_MACRO
+#define EMPTY_MACRO_FUN()
+
+void test() {
+ if (cond("if0") /*comment*/) do_something("same-line");
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if0") /*comment*/) { do_something("same-line");
+ // CHECK-FIXES: }
+
+ if (cond("if0.1"))
+ do_something("single-line");
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if0.1")) {
+ // CHECK-FIXES: }
+
+ if (cond("if1") /*comment*/)
+ // some comment
+ do_something("if1");
+ // CHECK-MESSAGES: :[[@LINE-3]]:31: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if1") /*comment*/) {
+ // CHECK-FIXES: }
+ if (cond("if2")) {
+ do_something("if2");
+ }
+ if (cond("if3"))
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if3")) {
+ // CHECK-FIXES: }
+
+ if (cond("if-else1"))
+ do_something("if-else1");
+ // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("if-else1")) {
+ // CHECK-FIXES: } else {
+ else
+ do_something("if-else1 else");
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: statement should be inside braces
+ // CHECK-FIXES: }
+ if (cond("if-else2")) {
+ do_something("if-else2");
+ } else {
+ do_something("if-else2 else");
+ }
+
+ if (cond("if-else if-else1"))
+ do_something("if");
+ // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: statement should be inside braces
+ // CHECK-FIXES: } else if (cond("else if1")) {
+ else if (cond("else if1"))
+ do_something("else if");
+ // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: statement should be inside braces
+ else
+ do_something("else");
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: statement should be inside braces
+ // CHECK-FIXES: }
+ if (cond("if-else if-else2")) {
+ do_something("if");
+ } else if (cond("else if2")) {
+ do_something("else if");
+ } else {
+ do_something("else");
+ }
+
+ for (;;)
+ do_something("for");
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
+ // CHECK-FIXES: for (;;) {
+ // CHECK-FIXES: }
+ for (;;) {
+ do_something("for");
+ }
+ for (;;)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
+ // CHECK-FIXES: for (;;) {
+ // CHECK-FIXES: }
+
+ int arr[4] = {1, 2, 3, 4};
+ for (int a : arr)
+ do_something("for-range");
+ // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
+ // CHECK-FIXES: for (int a : arr) {
+ // CHECK-FIXES: }
+ for (int a : arr) {
+ do_something("for-range");
+ }
+ for (int a : arr)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
+ // CHECK-FIXES: for (int a : arr) {
+ // CHECK-FIXES: }
+
+ while (cond("while1"))
+ do_something("while");
+ // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: statement should be inside braces
+ // CHECK-FIXES: while (cond("while1")) {
+ // CHECK-FIXES: }
+ while (cond("while2")) {
+ do_something("while");
+ }
+ while (false)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: statement should be inside braces
+ // CHECK-FIXES: while (false) {
+ // CHECK-FIXES: }
+
+ do
+ do_something("do1");
+ while (cond("do1"));
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: statement should be inside braces
+ // CHECK-FIXES: do {
+ // CHECK-FIXES: } while (cond("do1"));
+ do {
+ do_something("do2");
+ } while (cond("do2"));
+
+ do
+ ;
+ while (false);
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: statement should be inside braces
+ // CHECK-FIXES: do {
+ // CHECK-FIXES: } while (false);
+
+ if (cond("ifif1"))
+ // comment
+ if (cond("ifif2"))
+ // comment
+ /*comment*/ ; // comment
+ // CHECK-MESSAGES: :[[@LINE-5]]:21: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-4]]:23: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("ifif1")) {
+ // CHECK-FIXES: if (cond("ifif2")) {
+ // CHECK-FIXES: }
+ // CHECK-FIXES-NEXT: }
+
+ if (cond("ifif3"))
+ // comment
+ if (cond("ifif4")) {
+ // comment
+ /*comment*/; // comment
+ }
+ // CHECK-MESSAGES: :[[@LINE-6]]:21: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("ifif3")) {
+ // CHECK-FIXES: }
+
+ if (cond("ifif5"))
+ ; /* multi-line
+ comment */
+ // CHECK-MESSAGES: :[[@LINE-3]]:21: warning: statement should be inside braces
+ // CHECK-FIXES: if (cond("ifif5")) {
+ // CHECK-FIXES: }/* multi-line
+
+ if (1) while (2) if (3) for (;;) do ; while(false) /**/;/**/
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-4]]:35: warning: statement should be inside braces
+ // CHECK-MESSAGES: :[[@LINE-5]]:38: warning: statement should be inside braces
+ // CHECK-FIXES: if (1) { while (2) { if (3) { for (;;) { do { ; } while(false) /**/;/**/
+ // CHECK-FIXES-NEXT: }
+ // CHECK-FIXES-NEXT: }
+ // CHECK-FIXES-NEXT: }
+ // CHECK-FIXES-NEXT: }
+}
+
+void f(const char *p) {
+ if (!p)
+ f("\
+");
+ // CHECK-MESSAGES: :[[@LINE-3]]:10: warning: statement should be inside braces
+ // CHECK-FIXES: {{^ }}if (!p) {{{$}}
+ // CHECK-FIXES-NEXT: {{^ }}f("\{{$}}
+ // CHECK-FIXES-_NEXT: {{^}}");{{$}} FIXME: This breaks (http://llvm.org/PR26228)
+ // CHECK-FIXES-_NEXT: {{^}}}{{$}}
+}
+
+#define M(x) x
+
+int test_macros(bool b) {
+ if (b) {
+ return 1;
+ } else
+ M(return 2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: statement should be inside braces
+ // CHECK-FIXES: } else {
+ // CHECK-FIXES-NEXT: M(return 2);
+ // CHECK-FIXES-NEXT: }
+ M(
+ for (;;)
+ ;
+ );
+ // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+ // CHECK-FIXES: {{^}} for (;;) {{{$}}
+ // CHECK-FIXES-NEXT: {{^ ;$}}
+ // CHECK-FIXES-NEXT: {{^}$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-container-size-empty %t
+
+namespace std {
+template <typename T> struct vector {
+ vector();
+ bool operator==(const vector<T>& other) const;
+ bool operator!=(const vector<T>& other) const;
+ unsigned long size() const;
+ bool empty() const;
+};
+
+template <typename T> struct basic_string {
+ basic_string();
+ bool operator==(const basic_string<T>& other) const;
+ bool operator!=(const basic_string<T>& other) const;
+ bool operator==(const char *) const;
+ bool operator!=(const char *) const;
+ basic_string<T> operator+(const basic_string<T>& other) const;
+ unsigned long size() const;
+ bool empty() const;
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+
+inline namespace __v2 {
+template <typename T> struct set {
+ set();
+ bool operator==(const set<T>& other) const;
+ bool operator!=(const set<T>& other) const;
+ unsigned long size() const;
+ bool empty() const;
+};
+}
+
+}
+
+template <typename T>
+class TemplatedContainer {
+public:
+ bool operator==(const TemplatedContainer<T>& other) const;
+ bool operator!=(const TemplatedContainer<T>& other) const;
+ int size() const;
+ bool empty() const;
+};
+
+template <typename T>
+class PrivateEmpty {
+public:
+ bool operator==(const PrivateEmpty<T>& other) const;
+ bool operator!=(const PrivateEmpty<T>& other) const;
+ int size() const;
+private:
+ bool empty() const;
+};
+
+struct BoolSize {
+ bool size() const;
+ bool empty() const;
+};
+
+struct EnumSize {
+ enum E { one };
+ enum E size() const;
+ bool empty() const;
+};
+
+class Container {
+public:
+ bool operator==(const Container& other) const;
+ int size() const;
+ bool empty() const;
+};
+
+class Derived : public Container {
+};
+
+class Container2 {
+public:
+ int size() const;
+ bool empty() const { return size() == 0; }
+};
+
+class Container3 {
+public:
+ int size() const;
+ bool empty() const;
+};
+
+bool Container3::empty() const { return this->size() == 0; }
+
+class Container4 {
+public:
+ bool operator==(const Container4& rhs) const;
+ int size() const;
+ bool empty() const { return *this == Container4(); }
+};
+
+std::string s_func() {
+ return std::string();
+}
+
+int main() {
+ std::set<int> intSet;
+ std::string str;
+ std::string str2;
+ std::wstring wstr;
+ str.size() + 0;
+ str.size() - 0;
+ 0 + str.size();
+ 0 - str.size();
+ if (intSet.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
+ // CHECK-FIXES: {{^ }}if (intSet.empty()){{$}}
+ // CHECK-MESSAGES: :32:8: note: method 'set<int>'::empty() defined here
+ if (intSet == std::set<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness
+ // CHECK-FIXES: {{^ }}if (intSet.empty()){{$}}
+ // CHECK-MESSAGES: :32:8: note: method 'set<int>'::empty() defined here
+ if (s_func() == "")
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (s_func().empty()){{$}}
+ if (str.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (str.empty()){{$}}
+ if ((str + str2).size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if ((str + str2).empty()){{$}}
+ if (str == "")
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (str.empty()){{$}}
+ if (str + str2 == "")
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if ((str + str2).empty()){{$}}
+ if (wstr.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (wstr.empty()){{$}}
+ if (wstr == "")
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (wstr.empty()){{$}}
+ std::vector<int> vect;
+ if (vect.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (vect == std::vector<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (vect.size() != 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (vect != std::vector<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (0 == vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (0 != vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (std::vector<int>() == vect)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (std::vector<int>() != vect)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (vect.size() > 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (0 < vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (vect.size() < 1)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (1 > vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (vect.size() >= 1)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (1 <= vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+ if (vect.size() > 1) // no warning
+ ;
+ if (1 < vect.size()) // no warning
+ ;
+ if (vect.size() <= 1) // no warning
+ ;
+ if (1 >= vect.size()) // no warning
+ ;
+ if (!vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect.empty()){{$}}
+ if (vect.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect.empty()){{$}}
+
+ if (vect.empty())
+ ;
+
+ const std::vector<int> vect2;
+ if (vect2.size() != 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!vect2.empty()){{$}}
+
+ std::vector<int> *vect3 = new std::vector<int>();
+ if (vect3->size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect3->empty()){{$}}
+ if ((*vect3).size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if ((*vect3).empty()){{$}}
+ if ((*vect3) == std::vector<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect3->empty()){{$}}
+ if (*vect3 == std::vector<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect3->empty()){{$}}
+
+ delete vect3;
+
+ const std::vector<int> &vect4 = vect2;
+ if (vect4.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect4.empty()){{$}}
+ if (vect4 == std::vector<int>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (vect4.empty()){{$}}
+
+ TemplatedContainer<void> templated_container;
+ if (templated_container.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (templated_container == TemplatedContainer<void>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (templated_container.size() != 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (templated_container != TemplatedContainer<void>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (0 == templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (TemplatedContainer<void>() == templated_container)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (0 != templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (TemplatedContainer<void>() != templated_container)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (templated_container.size() > 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (0 < templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (templated_container.size() < 1)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (1 > templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (templated_container.size() >= 1)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (1 <= templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (templated_container.size() > 1) // no warning
+ ;
+ if (1 < templated_container.size()) // no warning
+ ;
+ if (templated_container.size() <= 1) // no warning
+ ;
+ if (1 >= templated_container.size()) // no warning
+ ;
+ if (!templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (templated_container.empty()){{$}}
+ if (templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+
+ if (templated_container.empty())
+ ;
+
+ // No warnings expected.
+ PrivateEmpty<void> private_empty;
+ if (private_empty.size() == 0)
+ ;
+ if (private_empty == PrivateEmpty<void>())
+ ;
+ if (private_empty.size() != 0)
+ ;
+ if (private_empty != PrivateEmpty<void>())
+ ;
+ if (0 == private_empty.size())
+ ;
+ if (PrivateEmpty<void>() == private_empty)
+ ;
+ if (0 != private_empty.size())
+ ;
+ if (PrivateEmpty<void>() != private_empty)
+ ;
+ if (private_empty.size() > 0)
+ ;
+ if (0 < private_empty.size())
+ ;
+ if (private_empty.size() < 1)
+ ;
+ if (1 > private_empty.size())
+ ;
+ if (private_empty.size() >= 1)
+ ;
+ if (1 <= private_empty.size())
+ ;
+ if (private_empty.size() > 1)
+ ;
+ if (1 < private_empty.size())
+ ;
+ if (private_empty.size() <= 1)
+ ;
+ if (1 >= private_empty.size())
+ ;
+ if (!private_empty.size())
+ ;
+ if (private_empty.size())
+ ;
+
+ // Types with weird size() return type.
+ BoolSize bs;
+ if (bs.size() == 0)
+ ;
+ EnumSize es;
+ if (es.size() == 0)
+ ;
+
+ Derived derived;
+ if (derived.size() == 0)
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (derived.empty()){{$}}
+ if (derived == Derived())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (derived.empty()){{$}}
+}
+
+#define CHECKSIZE(x) if (x.size())
+// CHECK-FIXES: #define CHECKSIZE(x) if (x.size())
+
+template <typename T> void f() {
+ std::vector<T> v;
+ if (v.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
+ // CHECK-FIXES: {{^ }}if (!v.empty()){{$}}
+ if (v == std::vector<T>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object [readability-container-size-empty]
+ // CHECK-FIXES: {{^ }}if (v.empty()){{$}}
+ // CHECK-FIXES-NEXT: ;
+ CHECKSIZE(v);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used
+ // CHECK-MESSAGES: CHECKSIZE(v);
+
+ TemplatedContainer<T> templated_container;
+ if (templated_container.size())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ if (templated_container != TemplatedContainer<T>())
+ ;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+ // CHECK-FIXES: {{^ }}if (!templated_container.empty()){{$}}
+ // CHECK-FIXES-NEXT: ;
+ CHECKSIZE(templated_container);
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used
+ // CHECK-MESSAGES: CHECKSIZE(templated_container);
+}
+
+void g() {
+ f<int>();
+ f<double>();
+ f<char *>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-delete-null-pointer %t
+
+#define NULL 0
+
+void f() {
+ int *p = 0;
+
+ // #1
+ if (p) { // #2
+ delete p;
+ } // #3
+ // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: 'if' statement is unnecessary; deleting null pointer has no effect [readability-delete-null-pointer]
+
+ // CHECK-FIXES: {{^ }}// #1
+ // CHECK-FIXES-NEXT: {{^ }}// #2
+ // CHECK-FIXES-NEXT: delete p;
+ // CHECK-FIXES-NEXT: {{^ }}// #3
+
+ int *p2 = new int[3];
+ // #4
+ if (p2) // #5
+ delete[] p2;
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 'if' statement is unnecessary;
+
+ // CHECK-FIXES: // #4
+ // CHECK-FIXES-NEXT: {{^ }}// #5
+ // CHECK-FIXES-NEXT: delete[] p2;
+
+ int *p3 = 0;
+ if (NULL != p3) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+ delete p3;
+ }
+ // CHECK-FIXES-NOT: if (NULL != p3) {
+ // CHECK-FIXES: delete p3;
+
+ int *p4 = nullptr;
+ if (p4 != nullptr) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+ delete p4;
+ }
+ // CHECK-FIXES-NOT: if (p4 != nullptr) {
+ // CHECK-FIXES: delete p4;
+
+ char *c;
+ if (c != 0) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+ delete c;
+ }
+ // CHECK-FIXES-NOT: if (c != 0) {
+ // CHECK-FIXES: delete c;
+
+ char *c2;
+ if (c2) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+ // CHECK-FIXES: } else {
+ // CHECK-FIXES: c2 = c;
+ delete c2;
+ } else {
+ c2 = c;
+ }
+ struct A {
+ void foo() {
+ if (mp) // #6
+ delete mp;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: 'if' statement is unnecessary; deleting null pointer has no effect [readability-delete-null-pointer]
+ // CHECK-FIXES: {{^ }}// #6
+ // CHECK-FIXES-NEXT: delete mp;
+ }
+ int *mp;
+ };
+}
+
+void g() {
+ int *p5, *p6;
+ if (p5)
+ delete p6;
+
+ if (p5 && p6)
+ delete p5;
+
+ if (p6) {
+ int x = 5;
+ delete p6;
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-deleted-default %t -- -- -std=c++11 -fno-ms-compatibility
+
+class NoDefault {
+public:
+ NoDefault() = delete;
+ NoDefault(NoDefault &&Other) = delete;
+ NoDefault(const NoDefault &Other) = delete;
+};
+
+class MissingEverything {
+public:
+ MissingEverything() = default;
+ // CHECK-MESSAGES: warning: default constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is lacking a default constructor; definition can either be removed or explicitly deleted [readability-deleted-default]
+ MissingEverything(MissingEverything &&Other) = default;
+ // CHECK-MESSAGES: warning: move constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is neither copyable nor movable; definition can either be removed or explicitly deleted [readability-deleted-default]
+ MissingEverything(const MissingEverything &Other) = default;
+ // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is not copyable; definition can either be removed or explicitly deleted [readability-deleted-default]
+ MissingEverything &operator=(MissingEverything &&Other) = default;
+ // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default]
+ MissingEverything &operator=(const MissingEverything &Other) = default;
+ // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default]
+
+private:
+ NoDefault ND;
+};
+
+class NotAssignable {
+public:
+ NotAssignable(NotAssignable &&Other) = default;
+ NotAssignable(const NotAssignable &Other) = default;
+ NotAssignable &operator=(NotAssignable &&Other) = default;
+ // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted
+ NotAssignable &operator=(const NotAssignable &Other) = default;
+ // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted
+
+private:
+ const int I = 0;
+};
+
+class Movable {
+public:
+ Movable() = default;
+ Movable(Movable &&Other) = default;
+ Movable(const Movable &Other) = delete;
+ Movable &operator=(Movable &&Other) = default;
+ Movable &operator=(const Movable &Other) = delete;
+};
+
+class NotCopyable {
+public:
+ NotCopyable(NotCopyable &&Other) = default;
+ NotCopyable(const NotCopyable &Other) = default;
+ // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted
+ NotCopyable &operator=(NotCopyable &&Other) = default;
+ NotCopyable &operator=(const NotCopyable &Other) = default;
+ // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted
+private:
+ Movable M;
+};
+
+template <typename T> class Templated {
+public:
+ // No warning here, it is a templated class.
+ Templated() = default;
+ Templated(Templated &&Other) = default;
+ Templated(const Templated &Other) = default;
+ Templated &operator=(Templated &&Other) = default;
+ Templated &operator=(const Templated &Other) = default;
+
+ class InnerTemplated {
+ public:
+ // This class is not in itself templated, but we still don't have warning.
+ InnerTemplated() = default;
+ InnerTemplated(InnerTemplated &&Other) = default;
+ InnerTemplated(const InnerTemplated &Other) = default;
+ InnerTemplated &operator=(InnerTemplated &&Other) = default;
+ InnerTemplated &operator=(const InnerTemplated &Other) = default;
+
+ private:
+ T TVar;
+ };
+
+ class InnerNotTemplated {
+ public:
+ // This one could technically have warnings, but currently doesn't.
+ InnerNotTemplated() = default;
+ InnerNotTemplated(InnerNotTemplated &&Other) = default;
+ InnerNotTemplated(const InnerNotTemplated &Other) = default;
+ InnerNotTemplated &operator=(InnerNotTemplated &&Other) = default;
+ InnerNotTemplated &operator=(const InnerNotTemplated &Other) = default;
+
+ private:
+ int I;
+ };
+
+private:
+ const T TVar{};
+};
+
+int FunctionWithInnerClass() {
+ class InnerNotAssignable {
+ public:
+ InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default;
+ // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted
+ private:
+ const int I = 0;
+ };
+ return 1;
+};
+
+template <typename T>
+int TemplateFunctionWithInnerClass() {
+ class InnerNotAssignable {
+ public:
+ InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default;
+ private:
+ const T TVar{};
+ };
+ return 1;
+};
+
+void Foo() {
+ Templated<const int> V1;
+ Templated<int>::InnerTemplated V2;
+ Templated<float>::InnerNotTemplated V3;
+ TemplateFunctionWithInnerClass<int>();
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++11 -fexceptions
+
+void f(int a) {
+ if (a > 0)
+ return;
+ else // comment-0
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'else' after 'return'
+ // CHECK-FIXES: {{^}} // comment-0
+ return;
+
+ if (a > 0) {
+ return;
+ } else { // comment-1
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+ // CHECK-FIXES: {{^}} } // comment-1
+ return;
+ }
+
+ if (a > 0) {
+ f(0);
+ if (a > 10)
+ return;
+ } else {
+ return;
+ }
+
+ if (a > 0)
+ f(0);
+ else if (a > 10)
+ return;
+ else // comment-2
+ // CHECK-FIXES-NOT: {{^}} // comment-2
+ f(0);
+
+ if (a > 0)
+ if (a < 10)
+ return;
+ else // comment-3
+ // CHECK-FIXES-NOT: {{^}} // comment-3
+ f(0);
+ else
+ if (a > 10)
+ return;
+ else // comment-4
+ // CHECK-FIXES-NOT: {{^}} // comment-4
+ f(0);
+
+ if (a > 0) {
+ if (a < 10)
+ return;
+ else // comment-5
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+ // CHECK-FIXES: {{^}} // comment-5
+ f(0);
+ } else {
+ if (a > 10)
+ return;
+ else // comment-6
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+ // CHECK-FIXES: {{^}} // comment-6
+ f(0);
+ }
+}
+
+void foo() {
+ for (unsigned x = 0; x < 42; ++x) {
+ if (x) {
+ continue;
+ } else { // comment-7
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'continue'
+ // CHECK-FIXES: {{^}} } // comment-7
+ x++;
+ }
+ if (x) {
+ break;
+ } else { // comment-8
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'break'
+ // CHECK-FIXES: {{^}} } // comment-8
+ x++;
+ }
+ if (x) {
+ throw 42;
+ } else { // comment-9
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'throw'
+ // CHECK-FIXES: {{^}} } // comment-9
+ x++;
+ }
+ }
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}, {key: readability-function-size.ParameterThreshold, value: 5}, {key: readability-function-size.NestingThreshold, value: 2}]}' -- -std=c++11
+
+// Bad formatting is intentional, don't run clang-format over the whole file!
+
+void foo1() {
+}
+
+void foo2() {;}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo2' exceeds recommended size/complexity thresholds [readability-function-size]
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0)
+
+void foo3() {
+;
+
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'foo3' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 1 statements (threshold 0)
+
+void foo4(int i) { if (i) {} else; {}
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo4' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 3 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 branches (threshold 0)
+
+void foo5(int i) {for(;i;)while(i)
+do;while(i);
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo5' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 7 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 3 branches (threshold 0)
+
+template <typename T> T foo6(T i) {return i;
+}
+int x = foo6(0);
+// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: function 'foo6' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: 1 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: 1 statements (threshold 0)
+
+void foo7(int p1, int p2, int p3, int p4, int p5, int p6) {;}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo7' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 6 parameters (threshold 5)
+
+void bar1() { [](){;;;;;;;;;;;if(1){}}();
+
+
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'bar1' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 14 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 1 branches (threshold 0)
+
+void bar2() { class A { void barx() {;;} }; }
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar2' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 3 statements (threshold 0)
+//
+// CHECK-MESSAGES: :[[@LINE-4]]:30: warning: function 'barx' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: 2 statements (threshold 0)
+
+#define macro() {int x; {int y; {int z;}}}
+
+void baz0() { // 1
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'baz0' exceeds recommended size/complexity
+ // CHECK-MESSAGES: :[[@LINE-2]]:6: note: 27 lines including whitespace and comments (threshold 0)
+ // CHECK-MESSAGES: :[[@LINE-3]]:6: note: 9 statements (threshold 0)
+ int a;
+ { // 2
+ int b;
+ { // 3
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
+ int c;
+ { // 4
+ int d;
+ }
+ }
+ }
+ { // 2
+ int e;
+ }
+ { // 2
+ { // 3
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
+ int j;
+ }
+ }
+ macro()
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: nesting level 3 starts here (threshold 2)
+// CHECK-MESSAGES: :[[@LINE-28]]:25: note: expanded from macro 'macro'
+}
+
+// check that nested if's are not reported. this was broken initially
+void nesting_if() { // 1
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'nesting_if' exceeds recommended size/complexity
+ // CHECK-MESSAGES: :[[@LINE-2]]:6: note: 22 lines including whitespace and comments (threshold 0)
+ // CHECK-MESSAGES: :[[@LINE-3]]:6: note: 18 statements (threshold 0)
+ // CHECK-MESSAGES: :[[@LINE-4]]:6: note: 6 branches (threshold 0)
+ if (true) { // 2
+ int j;
+ } else if (true) { // 2
+ int j;
+ if (true) { // 3
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: note: nesting level 3 starts here (threshold 2)
+ int j;
+ }
+ } else if (true) { // 2
+ int j;
+ if (true) { // 3
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: note: nesting level 3 starts here (threshold 2)
+ int j;
+ }
+ } else if (true) { // 2
+ int j;
+ }
+}
+
+// however this should warn
+void bad_if_nesting() { // 1
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bad_if_nesting' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 22 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 12 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 4 branches (threshold 0)
+ if (true) { // 2
+ int j;
+ } else { // 2
+ if (true) { // 3
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: note: nesting level 3 starts here (threshold 2)
+ int j;
+ } else { // 3
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: note: nesting level 3 starts here (threshold 2)
+ if (true) { // 4
+ int j;
+ } else { // 4
+ if (true) { // 5
+ int j;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Remove UNSUPPORTED for powerpc64le when the problem introduced by
+// r288563 is resolved.
+// UNSUPPORTED: powerpc64le
+// RUN: %check_clang_tidy %s readability-identifier-naming %t -- \
+// RUN: -config='{CheckOptions: [ \
+// RUN: {key: readability-identifier-naming.AbstractClassCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.AbstractClassPrefix, value: 'A'}, \
+// RUN: {key: readability-identifier-naming.ClassCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.ClassPrefix, value: 'C'}, \
+// RUN: {key: readability-identifier-naming.ClassConstantCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.ClassConstantPrefix, value: 'k'}, \
+// RUN: {key: readability-identifier-naming.ClassMemberCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.ClassMethodCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.ConstantCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.ConstantSuffix, value: '_CST'}, \
+// RUN: {key: readability-identifier-naming.ConstexprFunctionCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.ConstexprMethodCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.ConstexprVariableCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.EnumCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.EnumPrefix, value: 'E'}, \
+// RUN: {key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.FunctionCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.GlobalFunctionCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.GlobalVariableCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.GlobalVariablePrefix, value: 'g_'}, \
+// RUN: {key: readability-identifier-naming.InlineNamespaceCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.LocalConstantCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.LocalConstantPrefix, value: 'k'}, \
+// RUN: {key: readability-identifier-naming.LocalVariableCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.MemberCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.MemberPrefix, value: 'm_'}, \
+// RUN: {key: readability-identifier-naming.ConstantMemberCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.PrivateMemberPrefix, value: '__'}, \
+// RUN: {key: readability-identifier-naming.ProtectedMemberPrefix, value: '_'}, \
+// RUN: {key: readability-identifier-naming.PublicMemberCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.MethodCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.PrivateMethodPrefix, value: '__'}, \
+// RUN: {key: readability-identifier-naming.ProtectedMethodPrefix, value: '_'}, \
+// RUN: {key: readability-identifier-naming.NamespaceCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.ParameterCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.ParameterPrefix, value: 'a_'}, \
+// RUN: {key: readability-identifier-naming.ConstantParameterCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.ConstantParameterPrefix, value: 'i_'}, \
+// RUN: {key: readability-identifier-naming.ParameterPackCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.PureFunctionCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.PureMethodCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.StaticVariableCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.StaticVariablePrefix, value: 's_'}, \
+// RUN: {key: readability-identifier-naming.StructCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.TemplateParameterCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.TemplateTemplateParameterCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.TemplateUsingCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.TemplateUsingPrefix, value: 'u_'}, \
+// RUN: {key: readability-identifier-naming.TypeTemplateParameterCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.TypeTemplateParameterSuffix, value: '_t'}, \
+// RUN: {key: readability-identifier-naming.TypedefCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.TypedefSuffix, value: '_t'}, \
+// RUN: {key: readability-identifier-naming.UnionCase, value: CamelCase}, \
+// RUN: {key: readability-identifier-naming.UnionPrefix, value: 'U'}, \
+// RUN: {key: readability-identifier-naming.UsingCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.ValueTemplateParameterCase, value: camelBack}, \
+// RUN: {key: readability-identifier-naming.VariableCase, value: lower_case}, \
+// RUN: {key: readability-identifier-naming.VirtualMethodCase, value: Camel_Snake_Case}, \
+// RUN: {key: readability-identifier-naming.VirtualMethodPrefix, value: 'v_'}, \
+// RUN: {key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE}, \
+// RUN: {key: readability-identifier-naming.TypeAliasCase, value: camel_Snake_Back}, \
+// RUN: {key: readability-identifier-naming.TypeAliasSuffix, value: '_t'}, \
+// RUN: {key: readability-identifier-naming.IgnoreFailedSplit, value: 0} \
+// RUN: ]}' -- -std=c++11 -fno-delayed-template-parsing \
+// RUN: -I%S/Inputs/readability-identifier-naming \
+// RUN: -isystem %S/Inputs/readability-identifier-naming/system
+
+// clang-format off
+
+#include <system-header.h>
+#include "user-header.h"
+// NO warnings or fixes expected from declarations within header files without
+// the -header-filter= option
+
+namespace FOO_NS {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for namespace 'FOO_NS' [readability-identifier-naming]
+// CHECK-FIXES: {{^}}namespace foo_ns {{{$}}
+inline namespace InlineNamespace {
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: invalid case style for inline namespace 'InlineNamespace'
+// CHECK-FIXES: {{^}}inline namespace inline_namespace {{{$}}
+
+SYSTEM_NS::structure g_s1;
+// NO warnings or fixes expected as SYSTEM_NS and structure are declared in a header file
+
+USER_NS::object g_s2;
+// NO warnings or fixes expected as USER_NS and object are declared in a header file
+
+SYSTEM_MACRO(var1);
+// NO warnings or fixes expected as var1 is from macro expansion
+
+USER_MACRO(var2);
+// NO warnings or fixes expected as var2 is declared in a macro expansion
+
+#define BLA int FOO_bar
+BLA;
+// NO warnings or fixes expected as FOO_bar is from macro expansion
+
+int global0;
+#define USE_NUMBERED_GLOBAL(number) auto use_global##number = global##number
+USE_NUMBERED_GLOBAL(0);
+// NO warnings or fixes expected as global0 is pieced together in a macro
+// expansion.
+
+int global1;
+#define USE_NUMBERED_BAL(prefix, number) \
+ auto use_##prefix##bal##number = prefix##bal##number
+USE_NUMBERED_BAL(glo, 1);
+// NO warnings or fixes expected as global1 is pieced together in a macro
+// expansion.
+
+int global2;
+#define USE_RECONSTRUCTED(glo, bal) auto use_##glo##bal = glo##bal
+USE_RECONSTRUCTED(glo, bal2);
+// NO warnings or fixes expected as global2 is pieced together in a macro
+// expansion.
+
+int global;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'global'
+// CHECK-FIXES: {{^}}int g_global;{{$}}
+#define USE_IN_MACRO(m) auto use_##m = m
+USE_IN_MACRO(global);
+
+int global3;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'global3'
+// CHECK-FIXES: {{^}}int g_global3;{{$}}
+#define ADD_TO_SELF(m) (m) + (m)
+int g_twice_global3 = ADD_TO_SELF(global3);
+// CHECK-FIXES: {{^}}int g_twice_global3 = ADD_TO_SELF(g_global3);{{$}}
+
+enum my_enumeration {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for enum 'my_enumeration'
+// CHECK-FIXES: {{^}}enum EMyEnumeration {{{$}}
+ MyConstant = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'MyConstant'
+// CHECK-FIXES: {{^}} MY_CONSTANT = 1,{{$}}
+ your_CONST = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'your_CONST'
+// CHECK-FIXES: {{^}} YOUR_CONST = 1,{{$}}
+ THIS_ConstValue = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'THIS_ConstValue'
+// CHECK-FIXES: {{^}} THIS_CONST_VALUE = 1,{{$}}
+};
+
+constexpr int ConstExpr_variable = MyConstant;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for constexpr variable 'ConstExpr_variable'
+// CHECK-FIXES: {{^}}constexpr int const_expr_variable = MY_CONSTANT;{{$}}
+
+class my_class {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_class'
+// CHECK-FIXES: {{^}}class CMyClass {{{$}}
+public:
+ my_class();
+// CHECK-FIXES: {{^}} CMyClass();{{$}}
+
+ my_class(void*) : my_class() {}
+// CHECK-FIXES: {{^}} CMyClass(void*) : CMyClass() {}{{$}}
+
+ ~
+ my_class();
+// (space in destructor token test, we could check trigraph but they will be deprecated)
+// CHECK-FIXES: {{^}} ~{{$}}
+// CHECK-FIXES: {{^}} CMyClass();{{$}}
+
+private:
+ const int MEMBER_one_1 = ConstExpr_variable;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: invalid case style for constant member 'MEMBER_one_1'
+// CHECK-FIXES: {{^}} const int member_one_1 = const_expr_variable;{{$}}
+ int member2 = 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'member2'
+// CHECK-FIXES: {{^}} int __member2 = 2;{{$}}
+ int _memberWithExtraUnderscores_ = 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member '_memberWithExtraUnderscores_'
+// CHECK-FIXES: {{^}} int __memberWithExtraUnderscores = 42;{{$}}
+
+private:
+ int private_member = 3;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for private member 'private_member'
+// CHECK-FIXES: {{^}} int __private_member = 3;{{$}}
+
+protected:
+ int ProtMember;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for protected member 'ProtMember'
+// CHECK-FIXES: {{^}} int _ProtMember;{{$}}
+
+public:
+ int PubMem;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for public member 'PubMem'
+// CHECK-FIXES: {{^}} int pub_mem;{{$}}
+
+ static const int classConstant;
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: invalid case style for class constant 'classConstant'
+// CHECK-FIXES: {{^}} static const int kClassConstant;{{$}}
+ static int ClassMember_2;
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for class member 'ClassMember_2'
+// CHECK-FIXES: {{^}} static int ClassMember2;{{$}}
+};
+class my_class;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_class'
+// CHECK-FIXES: {{^}}class CMyClass;{{$}}
+
+class my_forward_declared_class; // No warning should be triggered.
+
+const int my_class::classConstant = 4;
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: invalid case style for class constant 'classConstant'
+// CHECK-FIXES: {{^}}const int CMyClass::kClassConstant = 4;{{$}}
+
+int my_class::ClassMember_2 = 5;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for class member 'ClassMember_2'
+// CHECK-FIXES: {{^}}int CMyClass::ClassMember2 = 5;{{$}}
+
+class my_derived_class : public virtual my_class {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_derived_class'
+// CHECK-FIXES: {{^}}class CMyDerivedClass : public virtual CMyClass {};{{$}}
+
+class CMyWellNamedClass {};
+// No warning expected as this class is well named.
+
+template <typename t_t>
+class CMyWellNamedClass2 : public my_class {
+ // CHECK-FIXES: {{^}}class CMyWellNamedClass2 : public CMyClass {{{$}}
+ t_t my_Bad_Member;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'my_Bad_Member'
+ // CHECK-FIXES: {{^}} t_t __my_Bad_Member;{{$}}
+ int my_Other_Bad_Member = 42;
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'my_Other_Bad_Member'
+ // CHECK-FIXES: {{^}} int __my_Other_Bad_Member = 42;{{$}}
+public:
+ CMyWellNamedClass2() = default;
+ CMyWellNamedClass2(CMyWellNamedClass2 const&) = default;
+ CMyWellNamedClass2(CMyWellNamedClass2 &&) = default;
+ CMyWellNamedClass2(t_t a_v, void *a_p) : my_class(a_p), my_Bad_Member(a_v) {}
+ // CHECK-FIXES: {{^}} CMyWellNamedClass2(t_t a_v, void *a_p) : CMyClass(a_p), __my_Bad_Member(a_v) {}{{$}}
+
+ CMyWellNamedClass2(t_t a_v) : my_class(), my_Bad_Member(a_v), my_Other_Bad_Member(11) {}
+ // CHECK-FIXES: {{^}} CMyWellNamedClass2(t_t a_v) : CMyClass(), __my_Bad_Member(a_v), __my_Other_Bad_Member(11) {}{{$}}
+};
+void InstantiateClassMethods() {
+ // Ensure we trigger the instantiation of each constructor
+ CMyWellNamedClass2<int> x;
+ CMyWellNamedClass2<int> x2 = x;
+ CMyWellNamedClass2<int> x3 = static_cast<CMyWellNamedClass2<int>&&>(x2);
+ CMyWellNamedClass2<int> x4(42);
+ CMyWellNamedClass2<int> x5(42, nullptr);
+}
+
+template<typename T>
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for type template parameter 'T'
+// CHECK-FIXES: {{^}}template<typename t_t>{{$}}
+class my_templated_class : CMyWellNamedClass {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_templated_class'
+// CHECK-FIXES: {{^}}class CMyTemplatedClass : CMyWellNamedClass {};{{$}}
+
+template<typename T>
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for type template parameter 'T'
+// CHECK-FIXES: {{^}}template<typename t_t>{{$}}
+class my_other_templated_class : my_templated_class< my_class>, private my_derived_class {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_other_templated_class'
+// CHECK-FIXES: {{^}}class CMyOtherTemplatedClass : CMyTemplatedClass< CMyClass>, private CMyDerivedClass {};{{$}}
+
+template<typename t_t>
+using mysuper_tpl_t = my_other_templated_class <:: FOO_NS ::my_class>;
+// CHECK-FIXES: {{^}}using mysuper_tpl_t = CMyOtherTemplatedClass <:: foo_ns ::CMyClass>;{{$}}
+
+const int global_Constant = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for global constant 'global_Constant'
+// CHECK-FIXES: {{^}}const int GLOBAL_CONSTANT = 6;{{$}}
+int Global_variable = 7;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'Global_variable'
+// CHECK-FIXES: {{^}}int g_global_variable = 7;{{$}}
+
+void global_function(int PARAMETER_1, int const CONST_parameter) {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for global function 'global_function'
+// CHECK-MESSAGES: :[[@LINE-2]]:26: warning: invalid case style for parameter 'PARAMETER_1'
+// CHECK-MESSAGES: :[[@LINE-3]]:49: warning: invalid case style for constant parameter 'CONST_parameter'
+// CHECK-FIXES: {{^}}void GlobalFunction(int a_parameter1, int const i_constParameter) {{{$}}
+ static const int THIS_static_ConsTant = 4;
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: invalid case style for static constant 'THIS_static_ConsTant'
+// CHECK-FIXES: {{^}} static const int THIS_STATIC_CONS_TANT = 4;{{$}}
+ static int THIS_static_variable;
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for static variable 'THIS_static_variable'
+// CHECK-FIXES: {{^}} static int s_thisStaticVariable;{{$}}
+ int const local_Constant = 3;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for local constant 'local_Constant'
+// CHECK-FIXES: {{^}} int const kLocalConstant = 3;{{$}}
+ int LOCAL_VARIABLE;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for local variable 'LOCAL_VARIABLE'
+// CHECK-FIXES: {{^}} int local_variable;{{$}}
+
+ int LOCAL_Array__[] = {0, 1, 2};
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for local variable 'LOCAL_Array__'
+// CHECK-FIXES: {{^}} int local_array[] = {0, 1, 2};{{$}}
+
+ for (auto _ : LOCAL_Array__) {
+ }
+}
+
+template<typename ... TYPE_parameters>
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: invalid case style for type template parameter 'TYPE_parameters'
+// CHECK-FIXES: {{^}}template<typename ... typeParameters_t>{{$}}
+void Global_Fun(TYPE_parameters... PARAMETER_PACK) {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for global function 'Global_Fun'
+// CHECK-MESSAGES: :[[@LINE-2]]:36: warning: invalid case style for parameter pack 'PARAMETER_PACK'
+// CHECK-FIXES: {{^}}void GlobalFun(typeParameters_t... parameterPack) {{{$}}
+ global_function(1, 2);
+// CHECK-FIXES: {{^}} GlobalFunction(1, 2);{{$}}
+ FOO_bar = Global_variable;
+// CHECK-FIXES: {{^}} FOO_bar = g_global_variable;{{$}}
+// NO fix expected for FOO_bar declared in macro expansion
+}
+
+template<template<typename> class TPL_parameter, int COUNT_params, typename ... TYPE_parameters>
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: invalid case style for template template parameter 'TPL_parameter'
+// CHECK-MESSAGES: :[[@LINE-2]]:54: warning: invalid case style for value template parameter 'COUNT_params'
+// CHECK-MESSAGES: :[[@LINE-3]]:81: warning: invalid case style for type template parameter 'TYPE_parameters'
+// CHECK-FIXES: {{^}}template<template<typename> class TplParameter, int countParams, typename ... typeParameters_t>{{$}}
+class test_CLASS {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'test_CLASS'
+// CHECK-FIXES: {{^}}class CTestClass {{{$}}
+};
+
+class abstract_class {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for abstract class 'abstract_class'
+// CHECK-FIXES: {{^}}class AAbstractClass {{{$}}
+ virtual ~abstract_class() = 0;
+// CHECK-FIXES: {{^}} virtual ~AAbstractClass() = 0;{{$}}
+ virtual void VIRTUAL_METHOD();
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: invalid case style for virtual method 'VIRTUAL_METHOD'
+// CHECK-FIXES: {{^}} virtual void v_Virtual_Method();{{$}}
+ void non_Virtual_METHOD() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for private method 'non_Virtual_METHOD'
+// CHECK-FIXES: {{^}} void __non_Virtual_METHOD() {}{{$}}
+
+public:
+ static void CLASS_METHOD() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: invalid case style for class method 'CLASS_METHOD'
+// CHECK-FIXES: {{^}} static void classMethod() {}{{$}}
+
+ constexpr int CST_expr_Method() { return 2; }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for constexpr method 'CST_expr_Method'
+// CHECK-FIXES: {{^}} constexpr int cst_expr_method() { return 2; }{{$}}
+
+private:
+ void PRIVate_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for private method 'PRIVate_Method'
+// CHECK-FIXES: {{^}} void __PRIVate_Method();{{$}}
+protected:
+ void protected_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for protected method 'protected_Method'
+// CHECK-FIXES: {{^}} void _protected_Method();{{$}}
+public:
+ void public_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for method 'public_Method'
+// CHECK-FIXES: {{^}} void publicMethod();{{$}}
+};
+
+constexpr int CE_function() { return 3; }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for constexpr function 'CE_function'
+// CHECK-FIXES: {{^}}constexpr int ce_function() { return 3; }{{$}}
+
+struct THIS___Structure {
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: invalid case style for struct 'THIS___Structure'
+// CHECK-FIXES: {{^}}struct this_structure {{{$}}
+ THIS___Structure();
+// CHECK-FIXES: {{^}} this_structure();{{$}}
+
+ union __MyUnion_is_wonderful__ {};
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for union '__MyUnion_is_wonderful__'
+// CHECK-FIXES: {{^}} union UMyUnionIsWonderful {};{{$}}
+};
+
+typedef THIS___Structure struct_type;
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: invalid case style for typedef 'struct_type'
+// CHECK-FIXES: {{^}}typedef this_structure struct_type_t;{{$}}
+
+struct_type GlobalTypedefTestFunction(struct_type a_argument1) {
+// CHECK-FIXES: {{^}}struct_type_t GlobalTypedefTestFunction(struct_type_t a_argument1) {
+ struct_type typedef_test_1;
+// CHECK-FIXES: {{^}} struct_type_t typedef_test_1;
+}
+
+using my_struct_type = THIS___Structure;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for type alias 'my_struct_type'
+// CHECK-FIXES: {{^}}using my_Struct_Type_t = this_structure;{{$}}
+
+template<typename t_t>
+using SomeOtherTemplate = my_other_templated_class <:: FOO_NS ::my_class>;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for type alias 'SomeOtherTemplate'
+// CHECK-FIXES: {{^}}using some_Other_Template_t = CMyOtherTemplatedClass <:: foo_ns ::CMyClass>;{{$}}
+
+static void static_Function() {
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: invalid case style for function 'static_Function'
+// CHECK-FIXES: {{^}}static void staticFunction() {{{$}}
+
+ ::FOO_NS::InlineNamespace::abstract_class::CLASS_METHOD();
+// CHECK-FIXES: {{^}} ::foo_ns::inline_namespace::AAbstractClass::classMethod();{{$}}
+ ::FOO_NS::InlineNamespace::static_Function();
+// CHECK-FIXES: {{^}} ::foo_ns::inline_namespace::staticFunction();{{$}}
+
+ using ::FOO_NS::InlineNamespace::CE_function;
+// CHECK-FIXES: {{^}} using ::foo_ns::inline_namespace::ce_function;{{$}}
+}
+
+#define MY_TEST_Macro(X) X()
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for macro definition 'MY_TEST_Macro'
+// CHECK-FIXES: {{^}}#define MY_TEST_MACRO(X) X()
+
+void MY_TEST_Macro(function) {}
+// CHECK-FIXES: {{^}}void MY_TEST_MACRO(function) {}
+}
+}
+
+template <typename t_t> struct a {
+ typename t_t::template b<> c;
+};
+
+template <template <typename> class A> struct b { A<int> c; };
--- /dev/null
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t \
+// RUN: -config='{CheckOptions: \
+// RUN: [{key: readability-implicit-bool-cast.AllowConditionalIntegerCasts, value: 1}, \
+// RUN: {key: readability-implicit-bool-cast.AllowConditionalPointerCasts, value: 1}]}' \
+// RUN: -- -std=c++11
+
+template<typename T>
+void functionTaking(T);
+
+int functionReturningInt();
+int* functionReturningPointer();
+
+struct Struct {
+ int member;
+};
+
+
+void regularImplicitCastIntegerToBoolIsNotIgnored() {
+ int integer = 0;
+ functionTaking<bool>(integer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool [readability-implicit-bool-cast]
+ // CHECK-FIXES: functionTaking<bool>(integer != 0);
+}
+
+void implicitCastIntegerToBoolInConditionalsIsAllowed() {
+ if (functionReturningInt()) {}
+ if (!functionReturningInt()) {}
+ if (functionReturningInt() && functionReturningPointer()) {}
+ if (!functionReturningInt() && !functionReturningPointer()) {}
+ for (; functionReturningInt(); ) {}
+ for (; functionReturningPointer(); ) {}
+ for (; functionReturningInt() && !functionReturningPointer() || (!functionReturningInt() && functionReturningPointer()); ) {}
+ while (functionReturningInt()) {}
+ while (functionReturningPointer()) {}
+ while (functionReturningInt() && !functionReturningPointer() || (!functionReturningInt() && functionReturningPointer())) {}
+ int value1 = functionReturningInt() ? 1 : 2;
+ int value2 = !functionReturningInt() ? 1 : 2;
+ int value3 = (functionReturningInt() && functionReturningPointer() || !functionReturningInt()) ? 1 : 2;
+ int value4 = functionReturningInt() ?: value3;
+ int *p1 = functionReturningPointer() ?: &value3;
+}
+
+void regularImplicitCastPointerToBoolIsNotIgnored() {
+ int* pointer = nullptr;
+ functionTaking<bool>(pointer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
+ // CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
+
+ int Struct::* memberPointer = &Struct::member;
+ functionTaking<bool>(memberPointer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
+ // CHECK-FIXES: functionTaking<bool>(memberPointer != nullptr);
+}
+
+void implicitCastPointerToBoolInConditionalsIsAllowed() {
+ if (functionReturningPointer()) {}
+ if (not functionReturningPointer()) {}
+ int value1 = functionReturningPointer() ? 1 : 2;
+ int value2 = (not functionReturningPointer()) ? 1 : 2;
+
+ int Struct::* memberPointer = &Struct::member;
+ if (memberPointer) {}
+ if (memberPointer) {}
+ int value3 = memberPointer ? 1 : 2;
+ int value4 = (not memberPointer) ? 1 : 2;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t -- -- -std=c++98
+
+// We need NULL macro, but some buildbots don't like including <cstddef> header
+// This is a portable way of getting it to work
+#undef NULL
+#define NULL 0L
+
+template<typename T>
+void functionTaking(T);
+
+struct Struct {
+ int member;
+};
+
+void useOldNullMacroInReplacements() {
+ int* pointer = NULL;
+ functionTaking<bool>(pointer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool [readability-implicit-bool-cast]
+ // CHECK-FIXES: functionTaking<bool>(pointer != 0);
+
+ int Struct::* memberPointer = NULL;
+ functionTaking<bool>(!memberPointer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast 'int Struct::*' -> bool
+ // CHECK-FIXES: functionTaking<bool>(memberPointer == 0);
+}
+
+void fixFalseLiteralConvertingToNullPointer() {
+ functionTaking<int*>(false);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int *'
+ // CHECK-FIXES: functionTaking<int*>(0);
+
+ int* pointer = NULL;
+ if (pointer == false) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: implicit cast bool -> 'int *'
+ // CHECK-FIXES: if (pointer == 0) {}
+
+ functionTaking<int Struct::*>(false);
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'int Struct::*'
+ // CHECK-FIXES: functionTaking<int Struct::*>(0);
+
+ int Struct::* memberPointer = NULL;
+ if (memberPointer != false) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int Struct::*'
+ // CHECK-FIXES: if (memberPointer != 0) {}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t
+
+// We need NULL macro, but some buildbots don't like including <cstddef> header
+// This is a portable way of getting it to work
+#undef NULL
+#define NULL 0L
+
+template<typename T>
+void functionTaking(T);
+
+struct Struct {
+ int member;
+};
+
+
+////////// Implicit cast from bool.
+
+void implicitCastFromBoolSimpleCases() {
+ bool boolean = true;
+
+ functionTaking<bool>(boolean);
+
+ functionTaking<int>(boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int' [readability-implicit-bool-cast]
+ // CHECK-FIXES: functionTaking<int>(static_cast<int>(boolean));
+
+ functionTaking<unsigned long>(boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
+ // CHECK-FIXES: functionTaking<unsigned long>(static_cast<unsigned long>(boolean));
+
+ functionTaking<char>(boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'char'
+ // CHECK-FIXES: functionTaking<char>(static_cast<char>(boolean));
+
+ functionTaking<float>(boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
+ // CHECK-FIXES: functionTaking<float>(static_cast<float>(boolean));
+
+ functionTaking<double>(boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
+ // CHECK-FIXES: functionTaking<double>(static_cast<double>(boolean));
+}
+
+float implicitCastFromBoolInReturnValue() {
+ bool boolean = false;
+ return boolean;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast bool -> 'float'
+ // CHECK-FIXES: return static_cast<float>(boolean);
+}
+
+void implicitCastFromBoolInSingleBoolExpressions(bool b1, bool b2) {
+ bool boolean = true;
+ boolean = b1 ^ b2;
+ boolean = b1 && b2;
+ boolean |= !b1 || !b2;
+ boolean &= b1;
+ boolean = b1 == true;
+ boolean = b2 != false;
+
+ int integer = boolean - 3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
+ // CHECK-FIXES: int integer = static_cast<int>(boolean) - 3;
+
+ float floating = boolean / 0.3f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
+ // CHECK-FIXES: float floating = static_cast<float>(boolean) / 0.3f;
+
+ char character = boolean;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'char'
+ // CHECK-FIXES: char character = static_cast<char>(boolean);
+}
+
+void implicitCastFromBoollInComplexBoolExpressions() {
+ bool boolean = true;
+ bool anotherBoolean = false;
+
+ int integer = boolean && anotherBoolean;
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
+ // CHECK-FIXES: int integer = static_cast<int>(boolean && anotherBoolean);
+
+ unsigned long unsignedLong = (! boolean) + 4ul;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast bool -> 'unsigned long'
+ // CHECK-FIXES: unsigned long unsignedLong = static_cast<unsigned long>(! boolean) + 4ul;
+
+ float floating = (boolean || anotherBoolean) * 0.3f;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
+ // CHECK-FIXES: float floating = static_cast<float>(boolean || anotherBoolean) * 0.3f;
+
+ double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit cast bool -> 'double'
+ // CHECK-FIXES: double doubleFloating = static_cast<double>(boolean && (anotherBoolean || boolean)) * 0.3;
+}
+
+void implicitCastFromBoolLiterals() {
+ functionTaking<int>(true);
+ // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int'
+ // CHECK-FIXES: functionTaking<int>(1);
+
+ functionTaking<unsigned long>(false);
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
+ // CHECK-FIXES: functionTaking<unsigned long>(0u);
+
+ functionTaking<signed char>(true);
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: implicit cast bool -> 'signed char'
+ // CHECK-FIXES: functionTaking<signed char>(1);
+
+ functionTaking<float>(false);
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
+ // CHECK-FIXES: functionTaking<float>(0.0f);
+
+ functionTaking<double>(true);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
+ // CHECK-FIXES: functionTaking<double>(1.0);
+}
+
+void implicitCastFromBoolInComparisons() {
+ bool boolean = true;
+ int integer = 0;
+
+ functionTaking<bool>(boolean == integer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int'
+ // CHECK-FIXES: functionTaking<bool>(static_cast<int>(boolean) == integer);
+
+ functionTaking<bool>(integer != boolean);
+ // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: implicit cast bool -> 'int'
+ // CHECK-FIXES: functionTaking<bool>(integer != static_cast<int>(boolean));
+}
+
+void ignoreBoolComparisons() {
+ bool boolean = true;
+ bool anotherBoolean = false;
+
+ functionTaking<bool>(boolean == anotherBoolean);
+ functionTaking<bool>(boolean != anotherBoolean);
+}
+
+void ignoreExplicitCastsFromBool() {
+ bool boolean = true;
+
+ int integer = static_cast<int>(boolean) + 3;
+ float floating = static_cast<float>(boolean) * 0.3f;
+ char character = static_cast<char>(boolean);
+}
+
+void ignoreImplicitCastFromBoolInMacroExpansions() {
+ bool boolean = true;
+
+ #define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3
+ int integerFromMacroBody = CAST_FROM_BOOL_IN_MACRO_BODY;
+
+ #define CAST_FROM_BOOL_IN_MACRO_ARGUMENT(x) x + 3
+ int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean);
+}
+
+namespace ignoreImplicitCastFromBoolInTemplateInstantiations {
+
+template<typename T>
+void templateFunction() {
+ bool boolean = true;
+ T uknownType = boolean + 3;
+}
+
+void useOfTemplateFunction() {
+ templateFunction<int>();
+}
+
+} // namespace ignoreImplicitCastFromBoolInTemplateInstantiations
+
+////////// Implicit cast to bool.
+
+void implicitCastToBoolSimpleCases() {
+ int integer = 10;
+ functionTaking<bool>(integer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>(integer != 0);
+
+ unsigned long unsignedLong = 10;
+ functionTaking<bool>(unsignedLong);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
+ // CHECK-FIXES: functionTaking<bool>(unsignedLong != 0u);
+
+ float floating = 0.0f;
+ functionTaking<bool>(floating);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: functionTaking<bool>(floating != 0.0f);
+
+ double doubleFloating = 1.0f;
+ functionTaking<bool>(doubleFloating);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+ // CHECK-FIXES: functionTaking<bool>(doubleFloating != 0.0);
+
+ signed char character = 'a';
+ functionTaking<bool>(character);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'signed char' -> bool
+ // CHECK-FIXES: functionTaking<bool>(character != 0);
+
+ int* pointer = nullptr;
+ functionTaking<bool>(pointer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
+ // CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
+
+ auto pointerToMember = &Struct::member;
+ functionTaking<bool>(pointerToMember);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
+ // CHECK-FIXES: functionTaking<bool>(pointerToMember != nullptr);
+}
+
+void implicitCastToBoolInSingleExpressions() {
+ int integer = 10;
+ bool boolComingFromInt = integer;
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: bool boolComingFromInt = integer != 0;
+
+ float floating = 10.0f;
+ bool boolComingFromFloat = floating;
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: bool boolComingFromFloat = floating != 0.0f;
+
+ signed char character = 'a';
+ bool boolComingFromChar = character;
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: implicit cast 'signed char' -> bool
+ // CHECK-FIXES: bool boolComingFromChar = character != 0;
+
+ int* pointer = nullptr;
+ bool boolComingFromPointer = pointer;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int *' -> bool
+ // CHECK-FIXES: bool boolComingFromPointer = pointer != nullptr;
+}
+
+void implicitCastToBoolInComplexExpressions() {
+ bool boolean = true;
+
+ int integer = 10;
+ int anotherInteger = 20;
+ bool boolComingFromInteger = integer + anotherInteger;
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: bool boolComingFromInteger = (integer + anotherInteger) != 0;
+
+ float floating = 0.2f;
+ bool boolComingFromFloating = floating - 0.3f || boolean;
+ // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: bool boolComingFromFloating = ((floating - 0.3f) != 0.0f) || boolean;
+
+ double doubleFloating = 0.3;
+ bool boolComingFromDoubleFloating = (doubleFloating - 0.4) && boolean;
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'double' -> bool
+ // CHECK-FIXES: bool boolComingFromDoubleFloating = ((doubleFloating - 0.4) != 0.0) && boolean;
+}
+
+void implicitCastInNegationExpressions() {
+ int integer = 10;
+ bool boolComingFromNegatedInt = !integer;
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: bool boolComingFromNegatedInt = integer == 0;
+
+ float floating = 10.0f;
+ bool boolComingFromNegatedFloat = ! floating;
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: bool boolComingFromNegatedFloat = floating == 0.0f;
+
+ signed char character = 'a';
+ bool boolComingFromNegatedChar = (! character);
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'signed char' -> bool
+ // CHECK-FIXES: bool boolComingFromNegatedChar = (character == 0);
+
+ int* pointer = nullptr;
+ bool boolComingFromNegatedPointer = not pointer;
+ // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: implicit cast 'int *' -> bool
+ // CHECK-FIXES: bool boolComingFromNegatedPointer = pointer == nullptr;
+}
+
+void implicitCastToBoolInControlStatements() {
+ int integer = 10;
+ if (integer) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: if (integer != 0) {}
+
+ long int longInteger = 0.2f;
+ for (;longInteger;) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit cast 'long' -> bool
+ // CHECK-FIXES: for (;longInteger != 0;) {}
+
+ float floating = 0.3f;
+ while (floating) {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: while (floating != 0.0f) {}
+
+ double doubleFloating = 0.4;
+ do {} while (doubleFloating);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: implicit cast 'double' -> bool
+ // CHECK-FIXES: do {} while (doubleFloating != 0.0);
+}
+
+bool implicitCastToBoolInReturnValue() {
+ float floating = 1.0f;
+ return floating;
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: return floating != 0.0f;
+}
+
+void implicitCastToBoolFromLiterals() {
+ functionTaking<bool>(0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>(false);
+
+ functionTaking<bool>(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+ functionTaking<bool>(2ul);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+
+ functionTaking<bool>(0.0f);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: functionTaking<bool>(false);
+
+ functionTaking<bool>(1.0f);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+ functionTaking<bool>(2.0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+
+ functionTaking<bool>('\0');
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
+ // CHECK-FIXES: functionTaking<bool>(false);
+
+ functionTaking<bool>('a');
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+
+ functionTaking<bool>("");
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+ functionTaking<bool>("abc");
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
+ // CHECK-FIXES: functionTaking<bool>(true);
+
+ functionTaking<bool>(NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'long' -> bool
+ // CHECK-FIXES: functionTaking<bool>(false);
+}
+
+void implicitCastToBoolFromUnaryMinusAndZeroLiterals() {
+ functionTaking<bool>(-0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>((-0) != 0);
+
+ functionTaking<bool>(-0.0f);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+ // CHECK-FIXES: functionTaking<bool>((-0.0f) != 0.0f);
+
+ functionTaking<bool>(-0.0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+ // CHECK-FIXES: functionTaking<bool>((-0.0) != 0.0);
+}
+
+void implicitCastToBoolInWithOverloadedOperators() {
+ struct UserStruct {
+ int operator()(int x) { return x; }
+ int operator+(int y) { return y; }
+ };
+
+ UserStruct s;
+
+ functionTaking<bool>(s(0));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>(s(0) != 0);
+
+ functionTaking<bool>(s + 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+ // CHECK-FIXES: functionTaking<bool>((s + 2) != 0);
+}
+
+int functionReturningInt();
+int* functionReturningPointer();
+
+void ignoreImplicitCastToBoolWhenDeclaringVariableInControlStatements() {
+ if (int integer = functionReturningInt()) {}
+
+ while (int* pointer = functionReturningPointer()) {}
+}
+
+void ignoreExplicitCastsToBool() {
+ int integer = 10;
+ bool boolComingFromInt = static_cast<bool>(integer);
+
+ float floating = 10.0f;
+ bool boolComingFromFloat = static_cast<bool>(floating);
+
+ char character = 'a';
+ bool boolComingFromChar = static_cast<bool>(character);
+
+ int* pointer = nullptr;
+ bool booleanComingFromPointer = static_cast<bool>(pointer);
+}
+
+void ignoreImplicitCastToBoolInMacroExpansions() {
+ int integer = 3;
+
+ #define CAST_TO_BOOL_IN_MACRO_BODY integer && false
+ bool boolFromMacroBody = CAST_TO_BOOL_IN_MACRO_BODY;
+
+ #define CAST_TO_BOOL_IN_MACRO_ARGUMENT(x) x || true
+ bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer);
+}
+
+namespace ignoreImplicitCastToBoolInTemplateInstantiations {
+
+template<typename T>
+void templateFunction() {
+ T unknownType = 0;
+ bool boolean = unknownType;
+}
+
+void useOfTemplateFunction() {
+ templateFunction<int>();
+}
+
+} // namespace ignoreImplicitCastToBoolInTemplateInstantiations
+
+namespace ignoreUserDefinedConversionOperator {
+
+struct StructWithUserConversion {
+ operator bool();
+};
+
+void useOfUserConversion() {
+ StructWithUserConversion structure;
+ functionTaking<bool>(structure);
+}
+
+} // namespace ignoreUserDefinedConversionOperator
--- /dev/null
+// RUN: %check_clang_tidy %s readability-inconsistent-declaration-parameter-name %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+void consistentFunction(int a, int b, int c);
+void consistentFunction(int a, int b, int c);
+void consistentFunction(int a, int b, int /*c*/);
+void consistentFunction(int /*c*/, int /*c*/, int /*c*/);
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'inconsistentFunction' has 2 other declarations with different parameter names [readability-inconsistent-declaration-parameter-name]
+void inconsistentFunction(int a, int b, int c);
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 1st inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('d', 'e', 'f'), in the other declaration: ('a', 'b', 'c')
+void inconsistentFunction(int d, int e, int f);
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 2nd inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('x', 'y', 'z'), in the other declaration: ('a', 'b', 'c')
+void inconsistentFunction(int x, int y, int z);
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name]
+// CHECK-MESSAGES: :[[@LINE+9]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('a'), in definition: ('c')
+// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c);
+void inconsistentFunctionWithVisibleDefinition(int a);
+// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+4]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('b'), in definition: ('c')
+// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c);
+void inconsistentFunctionWithVisibleDefinition(int b);
+void inconsistentFunctionWithVisibleDefinition(int c) { c; }
+
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function 'inconsidentFunctionWithUnreferencedParameterInDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+3]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('a'), in definition: ('b')
+void inconsidentFunctionWithUnreferencedParameterInDefinition(int a);
+void inconsidentFunctionWithUnreferencedParameterInDefinition(int b) {}
+
+//////////////////////////////////////////////////////
+
+struct Struct {
+// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Struct::inconsistentFunction' has a definition
+// CHECK-MESSAGES: :[[@LINE+6]]:14: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void inconsistentFunction(int b);
+ void inconsistentFunction(int a);
+};
+
+void Struct::inconsistentFunction(int b) { b = 0; }
+
+//////////////////////////////////////////////////////
+
+struct SpecialFunctions {
+// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: function 'SpecialFunctions::SpecialFunctions' has a definition
+// CHECK-MESSAGES: :[[@LINE+12]]:19: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:3: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: SpecialFunctions(int b);
+ SpecialFunctions(int a);
+
+// CHECK-MESSAGES: :[[@LINE+4]]:21: warning: function 'SpecialFunctions::operator=' has a definition
+// CHECK-MESSAGES: :[[@LINE+8]]:37: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:21: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: SpecialFunctions& operator=(const SpecialFunctions& b);
+ SpecialFunctions& operator=(const SpecialFunctions& a);
+};
+
+SpecialFunctions::SpecialFunctions(int b) { b; }
+
+SpecialFunctions& SpecialFunctions::operator=(const SpecialFunctions& b) { b; return *this; }
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+5]]:6: warning: function 'templateFunctionWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+7]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+3]]:6: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void templateFunctionWithSeparateDeclarationAndDefinition(T b);
+template<typename T>
+void templateFunctionWithSeparateDeclarationAndDefinition(T a);
+
+template<typename T>
+void templateFunctionWithSeparateDeclarationAndDefinition(T b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithSpecializations(T a) { a; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<int>' has a primary template declaration with different parameter names [readability-inconsistent-declaration-parameter-name]
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithSpecializations(int b) { b; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<float>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void templateFunctionWithSpecializations(float c) { c; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithoutDefinitionButWithSpecialization(T a);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithoutDefinitionButWithSpecialization<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithoutDefinitionButWithSpecialization(int b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(T a);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int b);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int c) { c; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+class ClassTemplate
+{
+public:
+// CHECK-MESSAGES: :[[@LINE+4]]:10: warning: function 'ClassTemplate::functionInClassTemplateWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+7]]:24: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:10: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void functionInClassTemplateWithSeparateDeclarationAndDefinition(int b);
+ void functionInClassTemplateWithSeparateDeclarationAndDefinition(int a);
+};
+
+template<typename T>
+void ClassTemplate<T>::functionInClassTemplateWithSeparateDeclarationAndDefinition(int b) { b; }
+
+//////////////////////////////////////////////////////
+
+class Class
+{
+public:
+ template<typename T>
+// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+12]]:13: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b);
+ void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T a);
+
+ template<typename T>
+ void memberFunctionTemplateWithSpecializations(T a) { a; }
+};
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-12]]:8: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void Class::memberFunctionTemplateWithSpecializations(int b) { b; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<float>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-18]]:8: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void Class::memberFunctionTemplateWithSpecializations(float c) { c; }
+
+//////////////////////////////////////////////////////
+
+#define DECLARE_FUNCTION_WITH_PARAM_NAME(function_name, param_name) \
+ void function_name(int param_name)
+
+// CHECK-MESSAGES: :[[@LINE+1]]:34: warning: function 'macroFunction' has 1 other declaration with different parameter names [readability-inconsistent-declaration-parameter-name]
+DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, a);
+// CHECK-MESSAGES: :[[@LINE+2]]:34: note: the 1st inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:34: note: differing parameters are named here: ('b'), in the other declaration: ('a')
+DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, b);
--- /dev/null
+// RUN: %check_clang_tidy %s readability-misleading-indentation %t
+
+void foo1();
+void foo2();
+
+#define BLOCK \
+ if (cond1) \
+ foo1(); \
+ foo2();
+
+int main()
+{
+ bool cond1 = true;
+ bool cond2 = true;
+
+ if (cond1)
+ if (cond2)
+ foo1();
+ else
+ foo2();
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: different indentation for 'if' and corresponding 'else' [readability-misleading-indentation]
+
+ if (cond1) {
+ if (cond2)
+ foo1();
+ }
+ else
+ foo2();
+
+ if (cond1)
+ if (cond2)
+ foo1();
+ else
+ foo2();
+
+ if (cond2)
+ foo1();
+ foo2();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: misleading indentation: statement is indented too deeply [readability-misleading-indentation]
+ // CHECK-MESSAGES: :[[@LINE-4]]:3: note: did you mean this line to be inside this 'if'
+ foo2(); // No redundant warning.
+
+ if (cond1)
+ {
+ foo1();
+ }
+ foo2();
+
+ if (cond1)
+ foo1();
+ foo2();
+
+ if (cond2)
+ if (cond1) foo1(); else foo2();
+
+ if (cond1) {
+ } else {
+ }
+
+ if (cond1) {
+ }
+ else {
+ }
+
+ if (cond1)
+ {
+ }
+ else
+ {
+ }
+
+ if (cond1)
+ {
+ }
+ else
+ {
+ }
+
+ if(cond1) {
+ }
+ else if (cond2) {
+ }
+ else {
+ }
+
+ if(cond1) {
+ }
+ else if (cond2) {
+ }
+ else {
+ }
+ // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: different indentation for 'if' and corresponding 'else' [readability-misleading-indentation]
+
+ if (cond1) {
+ if (cond1) {
+ }
+ else if (cond2) {
+ }
+ else {
+ }
+ if (cond1) {
+ } else if (cond2) {
+ } else if (!cond2) {
+ } else {
+ }
+ }
+ else if (cond2) {
+ }
+
+ BLOCK
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-misplaced-array-index %t
+
+#define ABC "abc"
+
+struct XY { int *X; int *Y; };
+
+void dostuff(int);
+
+void unusualSyntax(int *P1, struct XY *P2) {
+ 10[P1] = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: confusing array subscript expression, usually the index is inside the []
+ // CHECK-FIXES: P1[10] = 0;
+
+ 10[P2->X] = 0;
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: confusing array subscript expression
+ // CHECK-FIXES: P2->X[10] = 0;
+
+ dostuff(1["abc"]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+ // CHECK-FIXES: dostuff("abc"[1]);
+
+ dostuff(1[ABC]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+ // CHECK-FIXES: dostuff(ABC[1]);
+
+ dostuff(0[0 + ABC]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+ // CHECK-FIXES: dostuff(0[0 + ABC]);
+ // No fixit. Probably the code should be ABC[0]
+}
+
+void normalSyntax(int *X) {
+ X[10] = 0;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-named-parameter %t
+
+void Method(char *) { /* */ }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method(char * /*unused*/) { /* */ }
+void Method2(char *) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method2(char * /*unused*/) {}
+void Method3(char *, void *) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method3(char * /*unused*/, void * /*unused*/) {}
+void Method4(char *, int /*unused*/) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method4(char * /*unused*/, int /*unused*/) {}
+void operator delete[](void *) throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: all parameters should be named in a function
+// CHECK-FIXES: void operator delete[](void * /*unused*/) throw() {}
+int Method5(int) { return 0; }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: all parameters should be named in a function
+// CHECK-FIXES: int Method5(int /*unused*/) { return 0; }
+void Method6(void (*)(void *)) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method6(void (* /*unused*/)(void *)) {}
+template <typename T> void Method7(T) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: all parameters should be named in a function
+// CHECK-FIXES: template <typename T> void Method7(T /*unused*/) {}
+
+// Don't warn in macros.
+#define M void MethodM(int) {}
+M
+
+void operator delete(void *x) throw() {}
+void Method7(char * /*x*/) {}
+void Method8(char *x) {}
+typedef void (*TypeM)(int x);
+void operator delete[](void *x) throw();
+void operator delete[](void * /*x*/) throw();
+
+struct X {
+ X operator++(int) {}
+ X operator--(int) {}
+
+ X(X&) = delete;
+ X &operator=(X&) = default;
+
+ const int &i;
+};
+
+void (*Func1)(void *);
+void Func2(void (*func)(void *)) {}
+template <void Func(void *)> void Func3() {}
+
+template <typename T>
+struct Y {
+ void foo(T) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: all parameters should be named in a function
+// CHECK-FIXES: void foo(T /*unused*/) {}
+};
+
+Y<int> y;
+Y<float> z;
+
+struct Base {
+ virtual void foo(bool notThisOne);
+ virtual void foo(int argname);
+};
+
+struct Derived : public Base {
+ void foo(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: all parameters should be named in a function
+// CHECK-FIXES: void foo(int /*argname*/);
+};
+
+void FDef(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef(int /*n*/);
+void FDef(int n) {}
+
+void FDef2(int, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef2(int /*n*/, int /*unused*/);
+void FDef2(int n, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef2(int n, int /*unused*/) {}
+
+void FNoDef(int);
+
+class Z {};
+
+Z &operator++(Z&) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator++(Z& /*unused*/) {}
+
+Z &operator++(Z&, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator++(Z& /*unused*/, int) {}
+
+Z &operator--(Z&) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator--(Z& /*unused*/) {}
+
+Z &operator--(Z&, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) {}
+
+namespace testing {
+namespace internal {
+class IgnoredValue {
+ public:
+ template <typename T>
+ IgnoredValue(const T& /* ignored */) {}
+};
+}
+typedef internal::IgnoredValue Unused;
+}
+
+using ::testing::Unused;
+
+void MockFunction(Unused, int q, Unused) {
+ ++q;
+ ++q;
+ ++q;
+}
+
+namespace std {
+typedef decltype(nullptr) nullptr_t;
+}
+
+void f(std::nullptr_t) {}
+
+typedef void (F)(int);
+F f;
+void f(int x) {}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-non-const-parameter %t
+
+// Currently the checker only warns about pointer arguments.
+//
+// It can be defined both that the data is const and that the pointer is const,
+// the checker only checks if the data can be const-specified.
+//
+// It does not warn about pointers to records or function pointers.
+
+// Some external function where first argument is nonconst and second is const.
+char *strcpy1(char *dest, const char *src);
+unsigned my_strcpy(char *buf, const char *s);
+unsigned my_strlen(const char *buf);
+
+// CHECK-MESSAGES: :[[@LINE+1]]:29: warning: pointer parameter 'last' can be pointer to const [readability-non-const-parameter]
+void warn1(int *first, int *last) {
+ // CHECK-FIXES: {{^}}void warn1(int *first, const int *last) {{{$}}
+ *first = 0;
+ if (first < last) {
+ } // <- last can be const
+}
+
+// TODO: warning should be written here
+void warn2(char *p) {
+ char buf[10];
+ strcpy1(buf, p);
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:19: warning: pointer parameter 'p' can be
+void assign1(int *p) {
+ // CHECK-FIXES: {{^}}void assign1(const int *p) {{{$}}
+ const int *q;
+ q = p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:19: warning: pointer parameter 'p' can be
+void assign2(int *p) {
+ // CHECK-FIXES: {{^}}void assign2(const int *p) {{{$}}
+ const int *q;
+ q = p + 1;
+}
+
+void assign3(int *p) {
+ *p = 0;
+}
+
+void assign4(int *p) {
+ *p += 2;
+}
+
+void assign5(char *p) {
+ p[0] = 0;
+}
+
+void assign6(int *p) {
+ int *q;
+ q = p++;
+}
+
+void assign7(char *p) {
+ char *a, *b;
+ a = b = p;
+}
+
+void assign8(char *a, char *b) {
+ char *x;
+ x = (a ? a : b);
+}
+
+void assign9(unsigned char *str, const unsigned int i) {
+ unsigned char *p;
+ for (p = str + i; *p;) {
+ }
+}
+
+void assign10(int *buf) {
+ int i, *p;
+ for (i = 0, p = buf; i < 10; i++, p++) {
+ *p = 1;
+ }
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:17: warning: pointer parameter 'p' can be
+void init1(int *p) {
+ // CHECK-FIXES: {{^}}void init1(const int *p) {{{$}}
+ const int *q = p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:17: warning: pointer parameter 'p' can be
+void init2(int *p) {
+ // CHECK-FIXES: {{^}}void init2(const int *p) {{{$}}
+ const int *q = p + 1;
+}
+
+void init3(int *p) {
+ int *q = p;
+}
+
+void init4(float *p) {
+ int *q = (int *)p;
+}
+
+void init5(int *p) {
+ int *i = p ? p : 0;
+}
+
+void init6(int *p) {
+ int *a[] = {p, p, 0};
+}
+
+void init7(int *p, int x) {
+ for (int *q = p + x - 1; 0; q++)
+ ;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:18: warning: pointer parameter 'p' can be
+int return1(int *p) {
+ // CHECK-FIXES: {{^}}int return1(const int *p) {{{$}}
+ return *p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: pointer parameter 'p' can be
+const int *return2(int *p) {
+ // CHECK-FIXES: {{^}}const int *return2(const int *p) {{{$}}
+ return p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: pointer parameter 'p' can be
+const int *return3(int *p) {
+ // CHECK-FIXES: {{^}}const int *return3(const int *p) {{{$}}
+ return p + 1;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: pointer parameter 'p' can be
+const char *return4(char *p) {
+ // CHECK-FIXES: {{^}}const char *return4(const char *p) {{{$}}
+ return p ? p : "";
+}
+
+char *return5(char *s) {
+ return s;
+}
+
+char *return6(char *s) {
+ return s + 1;
+}
+
+char *return7(char *a, char *b) {
+ return a ? a : b;
+}
+
+char return8(int *p) {
+ return ++(*p);
+}
+
+void dontwarn1(int *p) {
+ ++(*p);
+}
+
+void dontwarn2(int *p) {
+ (*p)++;
+}
+
+int dontwarn3(_Atomic(int) * p) {
+ return *p;
+}
+
+void callFunction1(char *p) {
+ strcpy1(p, "abc");
+}
+
+void callFunction2(char *p) {
+ strcpy1(&p[0], "abc");
+}
+
+void callFunction3(char *p) {
+ strcpy1(p + 2, "abc");
+}
+
+char *callFunction4(char *p) {
+ return strcpy1(p, "abc");
+}
+
+unsigned callFunction5(char *buf) {
+ unsigned len = my_strlen(buf);
+ return len + my_strcpy(buf, "abc");
+}
+
+void f6(int **p);
+void callFunction6(int *p) { f6(&p); }
+
+typedef union { void *v; } t;
+void f7(t obj);
+void callFunction7(int *p) {
+ f7((t){p});
+}
+
+void f8(int &x);
+void callFunction8(int *p) {
+ f8(*p);
+}
+
+// Don't warn about nonconst function pointers that can be const.
+void functionpointer(double f(double), int x) {
+ f(x);
+}
+
+// TODO: This is a false positive.
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: pointer parameter 'p' can be
+int functionpointer2(int *p) {
+ return *p;
+}
+void use_functionpointer2() {
+ int (*fp)(int *) = functionpointer2; // <- the parameter 'p' can't be const
+}
+
+// Don't warn about nonconst record pointers that can be const.
+struct XY {
+ int *x;
+ int *y;
+};
+void recordpointer(struct XY *xy) {
+ *(xy->x) = 0;
+}
+
+class C {
+public:
+ C(int *p) : p(p) {}
+
+private:
+ int *p;
+};
+
+class C2 {
+public:
+ // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: pointer parameter 'p' can be
+ C2(int *p) : p(p) {}
+ // CHECK-FIXES: {{^}} C2(const int *p) : p(p) {}{{$}}
+
+private:
+ const int *p;
+};
+
+void tempObject(int *p) {
+ C c(p);
+}
+
+// avoid fp for const pointer array
+void constPointerArray(const char *remapped[][2]) {
+ const char *name = remapped[0][0];
+}
+
+class Warn {
+public:
+ // CHECK-MESSAGES: :[[@LINE+1]]:21: warning: pointer parameter 'p' can be
+ void doStuff(int *p) {
+ // CHECK-FIXES: {{^}} void doStuff(const int *p) {{{$}}
+ x = *p;
+ }
+
+private:
+ int x;
+};
+
+class Base {
+public:
+ // Ensure there is no false positive for this method. It is virtual.
+ virtual void doStuff(int *p) {
+ int x = *p;
+ }
+};
+
+class Derived : public Base {
+public:
+ // Ensure there is no false positive for this method. It overrides a method.
+ void doStuff(int *p) override {
+ int x = *p;
+ }
+};
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-control-flow %t
+
+void g(int i);
+void j();
+
+void f() {
+ return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement at the end of a function with a void return type [readability-redundant-control-flow]
+// CHECK-FIXES: {{^}}void f() {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void g() {
+ f();
+ return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^ }}f();{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void g(int i) {
+ if (i < 0) {
+ return;
+ }
+ if (i < 10) {
+ f();
+ }
+}
+
+int h() {
+ return 1;
+}
+
+void j() {
+}
+
+void k() {
+ for (int i = 0; i < 10; ++i) {
+ continue;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement at the end of loop statement
+// CHECK-FIXES: {{^}} for (int i = 0; i < 10; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void k2() {
+ int v[10] = { 0 };
+ for (auto i : v) {
+ continue;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}} for (auto i : v) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void m() {
+ int i = 0;
+ do {
+ ++i;
+ continue;
+ } while (i < 10);
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^ do {$}}
+// CHECK-FIXES-NEXT: {{^}} ++i;{{$}}
+// CHECK-FIXES-NEXT: {{^ *}}} while (i < 10);{{$}}
+
+void p() {
+ int i = 0;
+ while (i < 10) {
+ ++i;
+ continue;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}} while (i < 10) {{{$}}
+// CHECK-FIXES-NEXT: {{^}} ++i;{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void im_not_dead(int i) {
+ if (i > 0) {
+ return;
+ }
+ g();
+}
+
+void im_still_not_dead(int i) {
+ for (int j = 0; j < 10; ++j) {
+ if (i < 10) {
+ continue;
+ }
+ g();
+ }
+}
+
+void im_dead(int i) {
+ if (i > 0) {
+ return;
+ g();
+ }
+ g();
+}
+
+void im_still_dead(int i) {
+ for (int j = 0; j < 10; ++j) {
+ if (i < 10) {
+ continue;
+ g();
+ }
+ g();
+ }
+}
+
+void void_return() {
+ return g();
+}
+
+void nested_return_unmolested() {
+ g();
+ {
+ g();
+ return;
+ }
+}
+
+void nested_continue_unmolested() {
+ for (int i = 0; i < 10; ++i) {
+ if (i < 5) {
+ continue;
+ }
+ }
+}
+
+#define MACRO_RETURN_UNMOLESTED(fn_) \
+ (fn_)(); \
+ return
+
+#define MACRO_CONTINUE_UNMOLESTED(x_) \
+ do { \
+ for (int i = 0; i < (x_); ++i) { \
+ continue; \
+ } \
+ } while (false)
+
+void macro_return() {
+ MACRO_RETURN_UNMOLESTED(g);
+}
+
+void macro_continue() {
+ MACRO_CONTINUE_UNMOLESTED(10);
+}
+
+#define MACRO_RETURN_ARG(stmt_) \
+ stmt_
+
+#define MACRO_CONTINUE_ARG(stmt_) \
+ do { \
+ for (int i = 0; i < 10; ++i) { \
+ stmt_; \
+ } \
+ } while (false)
+
+void macro_arg_return() {
+ MACRO_RETURN_ARG(return);
+}
+
+void macro_arg_continue() {
+ MACRO_CONTINUE_ARG(continue);
+}
+
+template <typename T>
+void template_return(T check) {
+ if (check < T(0)) {
+ return;
+ }
+ return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^}} if (check < T(0)) {{{$}}
+// CHECK-FIXES-NEXT: {{^ return;$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <>
+void template_return(int check) {
+ if (check < 0) {
+ return;
+ }
+ return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^}} if (check < 0) {{{$}}
+// CHECK-FIXES-NEXT: {{^ return;$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <typename T>
+void template_loop(T end) {
+ for (T i = 0; i < end; ++i) {
+ continue;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}} for (T i = 0; i < end; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <>
+void template_loop(int end) {
+ for (int i = 0; i < end; ++i) {
+ continue;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}} for (int i = 0; i < end; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void call_templates() {
+ template_return(10);
+ template_return(10.0f);
+ template_return(10.0);
+ template_loop(10);
+ template_loop(10L);
+ template_loop(10U);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-declaration %t
+
+extern int Xyz;
+extern int Xyz; // Xyz
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Xyz' declaration [readability-redundant-declaration]
+// CHECK-FIXES: {{^}}// Xyz{{$}}
+int Xyz = 123;
+
+extern int A;
+extern int A, B;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'A' declaration
+// CHECK-FIXES: {{^}}extern int A, B;{{$}}
+
+extern int Buf[10];
+extern int Buf[10]; // Buf[10]
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Buf' declaration
+// CHECK-FIXES: {{^}}// Buf[10]{{$}}
+
+static int f();
+static int f(); // f
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'f' declaration
+// CHECK-FIXES: {{^}}// f{{$}}
+static int f() {}
+
+// Original check crashed for the code below.
+namespace std {
+typedef decltype(sizeof(0)) size_t;
+}
+void *operator new(std::size_t) __attribute__((__externally_visible__));
+void *operator new[](std::size_t) __attribute__((__externally_visible__));
+
+// Don't warn about static member definition.
+struct C {
+ static int I;
+};
+int C::I;
+
+template <class T>
+struct C2 {
+ C2();
+};
+
+template <class T>
+C2<T>::C2() = default;
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-function-ptr-dereference %t
+
+void f(int i);
+
+void positive() {
+ void (*p)(int) = f;
+
+ (**p)(1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: redundant repeated dereference of function pointer [readability-redundant-function-ptr-dereference]
+ // CHECK-FIXES: (*p)(1);
+ (*****p)(2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: redundant repeated
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: redundant repeated
+ // CHECK-MESSAGES: :[[@LINE-3]]:6: warning: redundant repeated
+ // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: redundant repeated
+ // CHECK-FIXES: (*p)(2);
+}
+
+void negative() {
+ void (*q)(int) = &f;
+
+ q(1);
+ (*q)(2);
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-member-init %t
+
+struct S {
+ S() = default;
+ S(int i) : i(i) {}
+ int i = 1;
+};
+
+struct T {
+ T(int i = 1) : i(i) {}
+ int i;
+};
+
+struct U {
+ int i;
+};
+
+union V {
+ int i;
+ double f;
+};
+
+// Initializer calls default constructor
+struct F1 {
+ F1() : f() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+ // CHECK-FIXES: F1() {}
+ S f;
+};
+
+// Initializer calls default constructor with default argument
+struct F2 {
+ F2() : f() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+ // CHECK-FIXES: F2() {}
+ T f;
+};
+
+// Multiple redundant initializers for same constructor
+struct F3 {
+ F3() : f(), g(1), h() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: initializer for member 'h' is redundant
+ // CHECK-FIXES: F3() : g(1) {}
+ S f, g, h;
+};
+
+// Templated class independent type
+template <class V>
+struct F4 {
+ F4() : f() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+ // CHECK-FIXES: F4() {}
+ S f;
+};
+F4<int> f4i;
+F4<S> f4s;
+
+// Base class
+struct F5 : S {
+ F5() : S() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'S' is redundant
+ // CHECK-FIXES: F5() {}
+};
+
+// Constructor call requires cleanup
+struct Cleanup {
+ ~Cleanup() {}
+};
+
+struct UsesCleanup {
+ UsesCleanup(const Cleanup &c = Cleanup()) {}
+};
+
+struct F6 {
+ F6() : uc() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'uc' is redundant
+ // CHECK-FIXES: F6() {}
+ UsesCleanup uc;
+};
+
+// Multiple inheritance
+struct F7 : S, T {
+ F7() : S(), T() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'S' is redundant
+ // CHECK-MESSAGES: :[[@LINE-2]]:15: warning: initializer for base class 'T' is redundant
+ // CHECK-FIXES: F7() {}
+};
+
+namespace Foo {
+inline namespace Bar {
+template <int N>
+struct Template {
+ Template() = default;
+ int i = N;
+};
+}
+}
+
+enum { N_THINGS = 5 };
+
+struct F8 : Foo::Template<N_THINGS> {
+ F8() : Template() {}
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'Foo::Template<N_THINGS>' is redundant
+ // CHECK-FIXES: F8() {}
+};
+
+// Initializer not written
+struct NF1 {
+ NF1() {}
+ S f;
+};
+
+// Initializer doesn't call default constructor
+struct NF2 {
+ NF2() : f(1) {}
+ S f;
+};
+
+// Initializer calls default constructor without using default argument
+struct NF3 {
+ NF3() : f(1) {}
+ T f;
+};
+
+// Initializer calls default constructor without using default argument
+struct NF4 {
+ NF4() : f(2) {}
+ T f;
+};
+
+// Initializer is zero-initialization
+struct NF5 {
+ NF5() : i() {}
+ int i;
+};
+
+// Initializer is direct-initialization
+struct NF6 {
+ NF6() : i(1) {}
+ int i;
+};
+
+// Initializer is aggregate initialization of struct
+struct NF7 {
+ NF7() : f{} {}
+ U f;
+};
+
+// Initializer is zero-initialization of struct
+struct NF7b {
+ NF7b() : f() {}
+ U f;
+};
+
+// Initializer is aggregate initialization of array
+struct NF8 {
+ NF8() : f{} {}
+ int f[2];
+};
+
+struct NF9 {
+ NF9() : f{} {}
+ S f[2];
+};
+
+// Initializing member of union
+union NF10 {
+ NF10() : s() {}
+ int i;
+ S s;
+};
+
+// Templated class dependent type
+template <class V>
+struct NF11 {
+ NF11() : f() {}
+ V f;
+};
+NF11<int> nf11i;
+NF11<S> nf11s;
+
+// Delegating constructor
+class NF12 {
+ NF12() = default;
+ NF12(int) : NF12() {}
+};
+
+// Const member
+struct NF13 {
+ NF13() : f() {}
+ const S f;
+};
+
+// Union member
+struct NF14 {
+ NF14() : f() {}
+ V f;
+};
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-smartptr-get %t
+
+#define NULL __null
+
+namespace std {
+
+template <typename T>
+struct unique_ptr {
+ T& operator*() const;
+ T* operator->() const;
+ T* get() const;
+};
+
+template <typename T>
+struct shared_ptr {
+ T& operator*() const;
+ T* operator->() const;
+ T* get() const;
+};
+
+} // namespace std
+
+struct Bar {
+ void Do();
+ void ConstDo() const;
+};
+struct BarPtr {
+ Bar* operator->();
+ Bar& operator*();
+ Bar* get();
+};
+struct int_ptr {
+ int* get();
+ int* operator->();
+ int& operator*();
+};
+
+struct Fail1 {
+ Bar* get();
+};
+struct Fail2 {
+ Bar* get();
+ int* operator->();
+ int& operator*();
+};
+
+struct PointerWithOverloadedGet {
+ int* get();
+ template <typename T>
+ T* get();
+ int* operator->();
+ int& operator*();
+};
+
+void Positive() {
+ BarPtr u;
+ // CHECK-FIXES: BarPtr u;
+ BarPtr().get()->Do();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant get() call on smart pointer [readability-redundant-smartptr-get]
+ // CHECK-MESSAGES: BarPtr().get()->Do();
+ // CHECK-FIXES: BarPtr()->Do();
+
+ u.get()->ConstDo();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant get() call
+ // CHECK-MESSAGES: u.get()->ConstDo();
+ // CHECK-FIXES: u->ConstDo();
+
+ Bar& b = *BarPtr().get();
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant get() call
+ // CHECK-MESSAGES: Bar& b = *BarPtr().get();
+ // CHECK-FIXES: Bar& b = *BarPtr();
+
+ Bar& b2 = *std::unique_ptr<Bar>().get();
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant get() call
+ // CHECK-MESSAGES: Bar& b2 = *std::unique_ptr<Bar>().get();
+ // CHECK-FIXES: Bar& b2 = *std::unique_ptr<Bar>();
+
+ (*BarPtr().get()).ConstDo();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+ // CHECK-MESSAGES: (*BarPtr().get()).ConstDo();
+ // CHECK-FIXES: (*BarPtr()).ConstDo();
+
+ (*std::unique_ptr<Bar>().get()).ConstDo();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+ // CHECK-MESSAGES: (*std::unique_ptr<Bar>().get()).ConstDo();
+ // CHECK-FIXES: (*std::unique_ptr<Bar>()).ConstDo();
+
+ std::unique_ptr<Bar>* up;
+ (*up->get()).Do();
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+ // CHECK-MESSAGES: (*up->get()).Do();
+ // CHECK-FIXES: (**up).Do();
+
+ int_ptr ip;
+ int i = *ip.get();
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant get() call
+ // CHECK-MESSAGES: int i = *ip.get();
+ // CHECK-FIXES: int i = *ip;
+
+ std::unique_ptr<int> uu;
+ std::shared_ptr<double> *ss;
+ bool bb = uu.get() == nullptr;
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant get() call
+ // CHECK-MESSAGES: uu.get() == nullptr;
+ // CHECK-FIXES: bool bb = uu == nullptr;
+
+ bb = nullptr != ss->get();
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant get() call
+ // CHECK-MESSAGES: nullptr != ss->get();
+ // CHECK-FIXES: bb = nullptr != *ss;
+
+ i = *PointerWithOverloadedGet().get();
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+ // CHECK-MESSAGES: i = *PointerWithOverloadedGet().get();
+ // CHECK-FIXES: i = *PointerWithOverloadedGet();
+
+ bb = std::unique_ptr<int>().get() == NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+ // CHECK-MESSAGES: bb = std::unique_ptr<int>().get() == NULL;
+ // CHECK-FIXES: bb = std::unique_ptr<int>() == NULL;
+ bb = ss->get() == NULL;
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+ // CHECK-MESSAGES: bb = ss->get() == NULL;
+ // CHECK-FIXES: bb = *ss == NULL;
+
+ std::unique_ptr<int> x, y;
+ if (x.get() == nullptr);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+ // CHECK-MESSAGES: if (x.get() == nullptr);
+ // CHECK-FIXES: if (x == nullptr);
+ if (nullptr == y.get());
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant get() call
+ // CHECK-MESSAGES: if (nullptr == y.get());
+ // CHECK-FIXES: if (nullptr == y);
+ if (x.get() == NULL);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+ // CHECK-MESSAGES: if (x.get() == NULL);
+ // CHECK-FIXES: if (x == NULL);
+ if (NULL == x.get());
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant get() call
+ // CHECK-MESSAGES: if (NULL == x.get());
+ // CHECK-FIXES: if (NULL == x);
+ if (x.get());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+ // CHECK-MESSAGES: if (x.get());
+ // CHECK-FIXES: if (x);
+}
+
+void Negative() {
+ struct NegPtr {
+ int* get();
+ int* operator->() {
+ return &*this->get();
+ }
+ int& operator*() {
+ return *get();
+ }
+ };
+
+ long l = *PointerWithOverloadedGet().get<long>();
+
+ std::unique_ptr<Bar>* u;
+ u->get()->Do();
+
+ Fail1().get()->Do();
+ Fail2().get()->Do();
+ const Bar& b = *Fail1().get();
+ (*Fail2().get()).Do();
+
+ int_ptr ip;
+ bool bb = ip.get() == nullptr;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-string-cstr %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ basic_string();
+ // MSVC headers define two constructors instead of using optional arguments.
+ basic_string(const C *p);
+ basic_string(const C *p, const A &a);
+ const C *c_str() const;
+ const C *data() const;
+};
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+}
+namespace llvm {
+struct StringRef {
+ StringRef(const char *p);
+ StringRef(const std::string &);
+};
+}
+
+void f1(const std::string &s) {
+ f1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}f1(s);{{$}}
+ f1(s.data());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}f1(s);{{$}}
+}
+void f2(const llvm::StringRef r) {
+ std::string s;
+ f2(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ }}f2(s);{{$}}
+}
+void f3(const llvm::StringRef &r) {
+ std::string s;
+ f3(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ }}f3(s);{{$}}
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-string-cstr %t -- -- -std=c++11
+
+typedef unsigned __INT16_TYPE__ char16;
+typedef unsigned __INT32_TYPE__ char32;
+typedef __SIZE_TYPE__ size;
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ typedef basic_string<C, T, A> _Type;
+ basic_string();
+ basic_string(const C *p, const A &a = A());
+
+ const C *c_str() const;
+ const C *data() const;
+
+ _Type& append(const C *s);
+ _Type& append(const C *s, size n);
+ _Type& assign(const C *s);
+ _Type& assign(const C *s, size n);
+
+ int compare(const _Type&) const;
+ int compare(const C* s) const;
+ int compare(size pos, size len, const _Type&) const;
+ int compare(size pos, size len, const C* s) const;
+
+ size find(const _Type& str, size pos = 0) const;
+ size find(const C* s, size pos = 0) const;
+ size find(const C* s, size pos, size n) const;
+
+ _Type& insert(size pos, const _Type& str);
+ _Type& insert(size pos, const C* s);
+ _Type& insert(size pos, const C* s, size n);
+
+ _Type& operator+=(const _Type& str);
+ _Type& operator+=(const C* s);
+ _Type& operator=(const _Type& str);
+ _Type& operator=(const C* s);
+};
+
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>> wstring;
+typedef basic_string<char16, std::char_traits<char16>, std::allocator<char16>> u16string;
+typedef basic_string<char32, std::char_traits<char32>, std::allocator<char32>> u32string;
+}
+
+std::string operator+(const std::string&, const std::string&);
+std::string operator+(const std::string&, const char*);
+std::string operator+(const char*, const std::string&);
+
+bool operator==(const std::string&, const std::string&);
+bool operator==(const std::string&, const char*);
+bool operator==(const char*, const std::string&);
+
+namespace llvm {
+struct StringRef {
+ StringRef(const char *p);
+ StringRef(const std::string &);
+};
+}
+
+// Tests for std::string.
+
+void f1(const std::string &s) {
+ f1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}f1(s);{{$}}
+ f1(s.data());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}f1(s);{{$}}
+}
+void f2(const llvm::StringRef r) {
+ std::string s;
+ f2(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ }}f2(s);{{$}}
+}
+void f3(const llvm::StringRef &r) {
+ std::string s;
+ f3(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}std::string s;{{$}}
+ // CHECK-FIXES-NEXT: {{^ }}f3(s);{{$}}
+}
+void f4(const std::string &s) {
+ const std::string* ptr = &s;
+ f1(ptr->c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}f1(*ptr);{{$}}
+}
+void f5(const std::string &s) {
+ std::string tmp;
+ tmp.append(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp.append(s);{{$}}
+ tmp.assign(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp.assign(s);{{$}}
+
+ if (tmp.compare(s.c_str()) == 0) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp.compare(s) == 0) return;{{$}}
+
+ if (tmp.compare(1, 2, s.c_str()) == 0) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp.compare(1, 2, s) == 0) return;{{$}}
+
+ if (tmp.find(s.c_str()) == 0) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp.find(s) == 0) return;{{$}}
+
+ if (tmp.find(s.c_str(), 2) == 0) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp.find(s, 2) == 0) return;{{$}}
+
+ if (tmp.find(s.c_str(), 2) == 0) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp.find(s, 2) == 0) return;{{$}}
+
+ tmp.insert(1, s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp.insert(1, s);{{$}}
+
+ tmp = s.c_str();
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp = s;{{$}}
+
+ tmp += s.c_str();
+ // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp += s;{{$}}
+
+ if (tmp == s.c_str()) return;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}if (tmp == s) return;{{$}}
+
+ tmp = s + s.c_str();
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp = s + s;{{$}}
+
+ tmp = s.c_str() + s;
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}}
+ // CHECK-FIXES: {{^ }}tmp = s + s;{{$}}
+}
+void f6(const std::string &s) {
+ std::string tmp;
+ tmp.append(s.c_str(), 2);
+ tmp.assign(s.c_str(), 2);
+
+ if (tmp.compare(s) == 0) return;
+ if (tmp.compare(1, 2, s) == 0) return;
+
+ tmp = s;
+ tmp += s;
+
+ if (tmp == s)
+ return;
+
+ tmp = s + s;
+
+ if (tmp.find(s.c_str(), 2, 4) == 0) return;
+
+ tmp.insert(1, s);
+ tmp.insert(1, s.c_str(), 2);
+}
+
+// Tests for std::wstring.
+
+void g1(const std::wstring &s) {
+ g1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}g1(s);{{$}}
+}
+
+// Tests for std::u16string.
+
+void h1(const std::u16string &s) {
+ h1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}h1(s);{{$}}
+}
+
+// Tests for std::u32string.
+
+void k1(const std::u32string &s) {
+ k1(s.c_str());
+ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+ // CHECK-FIXES: {{^ }}k1(s);{{$}}
+}
+
+// Tests on similar classes that aren't good candidates for this checker.
+
+struct NotAString {
+ NotAString();
+ NotAString(const NotAString&);
+ const char *c_str() const;
+};
+
+void dummy(const char*) {}
+
+void invalid(const NotAString &s) {
+ dummy(s.c_str());
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-string-init %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+struct basic_string {
+ basic_string();
+ basic_string(const basic_string&);
+ // MSVC headers define two constructors instead of using optional arguments.
+ basic_string(const C *);
+ basic_string(const C *, const A &);
+ ~basic_string();
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f() {
+ std::string a = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization [readability-redundant-string-init]
+ // CHECK-FIXES: std::string a;
+ std::string b("");
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string b;
+ std::string c = R"()";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string c;
+ std::string d(R"()");
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string d;
+
+ std::string u = "u";
+ std::string w("w");
+ std::string x = R"(x)";
+ std::string y(R"(y)");
+ std::string z;
+}
+
+void g() {
+ std::wstring a = L"";
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring a;
+ std::wstring b(L"");
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring b;
+ std::wstring c = LR"()";
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring c;
+ std::wstring d(LR"()");
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring d;
+
+ std::wstring u = L"u";
+ std::wstring w(L"w");
+ std::wstring x = LR"(x)";
+ std::wstring y(LR"(y)");
+ std::wstring z;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-redundant-string-init %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+struct basic_string {
+ basic_string();
+ basic_string(const basic_string&);
+ basic_string(const C *, const A &a = A());
+ ~basic_string();
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f() {
+ std::string a = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization [readability-redundant-string-init]
+ // CHECK-FIXES: std::string a;
+ std::string b("");
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string b;
+ std::string c = R"()";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string c;
+ std::string d(R"()");
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string d;
+
+ std::string u = "u";
+ std::string w("w");
+ std::string x = R"(x)";
+ std::string y(R"(y)");
+ std::string z;
+}
+
+void g() {
+ std::wstring a = L"";
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring a;
+ std::wstring b(L"");
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring b;
+ std::wstring c = LR"()";
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring c;
+ std::wstring d(LR"()");
+ // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+ // CHECK-FIXES: std::wstring d;
+
+ std::wstring u = L"u";
+ std::wstring w(L"w");
+ std::wstring x = LR"(x)";
+ std::wstring y(LR"(y)");
+ std::wstring z;
+}
+
+template <typename T>
+void templ() {
+ std::string s = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string s;
+}
+
+#define M(x) x
+#define N { std::string s = ""; }
+// CHECK-FIXES: #define N { std::string s = ""; }
+
+void h() {
+ templ<int>();
+ templ<double>();
+
+ M({ std::string s = ""; })
+ // CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
+ // CHECK-FIXES: M({ std::string s; })
+
+ N
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: redundant string initialization
+ // CHECK-FIXES: N
+ N
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: redundant string initialization
+ // CHECK-FIXES: N
+}
+
+typedef std::string MyString;
+#define STRING MyString
+#define DECL_STRING(name, val) STRING name = val
+
+void i() {
+ MyString a = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: redundant string initialization
+ // CHECK-FIXES: MyString a;
+ STRING b = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: redundant string initialization
+ // CHECK-FIXES: STRING b;
+ MyString c = "" "" "";
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: redundant string initialization
+ // CHECK-FIXES: MyString c;
+ STRING d = "" "" "";
+ // CHECK-MESSAGES: [[@LINE-1]]:10: warning: redundant string initialization
+ // CHECK-FIXES: STRING d;
+ DECL_STRING(e, "");
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+
+ MyString f = "u";
+ STRING g = "u";
+ DECL_STRING(h, "u");
+}
+
+#define EMPTY_STR ""
+void j() {
+ std::string a(EMPTY_STR);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string a;
+ std::string b = (EMPTY_STR);
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-FIXES: std::string b;
+
+ std::string c(EMPTY_STR "u" EMPTY_STR);
+}
+
+void k() {
+ std::string a = "", b = "", c = "";
+ // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+ // CHECK-MESSAGES: [[@LINE-2]]:23: warning: redundant string initialization
+ // CHECK-MESSAGES: [[@LINE-3]]:31: warning: redundant string initialization
+ // CHECK-FIXES: std::string a, b, c;
+
+ std::string d = "u", e = "u", f = "u";
+}
+
+// These cases should not generate warnings.
+extern void Param1(std::string param = "");
+extern void Param2(const std::string& param = "");
+void Param3(std::string param = "") {}
+void Param4(STRING param = "") {}
+
--- /dev/null
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{key: "readability-simplify-boolean-expr.ChainedConditionalAssignment", value: 1}]}" --
+
+void chained_conditional_compound_assignment(int i) {
+ bool b;
+ if (i < 0) {
+ b = true;
+ } else if (i < 10) {
+ b = false;
+ } else if (i > 20) {
+ b = true;
+ } else {
+ b = false;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: redundant boolean literal in conditional assignment [readability-simplify-boolean-expr]
+ // CHECK-FIXES: {{^}} } else if (i < 10) {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} b = false;{{$}}
+ // CHECK-FIXES-NEXT: {{^}} } else b = i > 20;{{$}}
+}
+
+void chained_conditional_assignment(int i) {
+ bool b;
+ if (i < 0)
+ b = true;
+ else if (i < 10)
+ b = false;
+ else if (i > 20)
+ b = true;
+ else
+ b = false;
+ // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: {{.*}} in conditional assignment
+ // CHECK-FIXES: {{^}} else if (i < 10)
+ // CHECK-FIXES-NEXT: {{^}} b = false;
+ // CHECK-FIXES-NEXT: {{^}} else b = i > 20;
+}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{key: "readability-simplify-boolean-expr.ChainedConditionalReturn", value: 1}]}" --
+
+bool chained_conditional_compound_return(int i) {
+ if (i < 0) {
+ return true;
+ } else if (i < 10) {
+ return false;
+ } else if (i > 20) {
+ return true;
+ } else {
+ return false;
+ }
+ // CHECK-MESSAGES: :[[@LINE-4]]:12: warning: redundant boolean literal in conditional return statement [readability-simplify-boolean-expr]
+ // CHECK-FIXES: {{^}} } else if (i < 10) {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} return false;{{$}}
+ // CHECK-FIXES-NEXT: {{^}} } else return i > 20;{{$}}
+}
+
+bool chained_conditional_return(int i) {
+ if (i < 0)
+ return true;
+ else if (i < 10)
+ return false;
+ else if (i > 20)
+ return true;
+ else
+ return false;
+ // CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return statement
+ // CHECK-FIXES: {{^}} else if (i < 10)
+ // CHECK-FIXES-NEXT: {{^}} return false;
+ // CHECK-FIXES-NEXT: {{^}} else return i > 20;
+}
+
+bool chained_simple_if_return(int i) {
+ if (i < 5)
+ return true;
+ if (i > 10)
+ return true;
+ return false;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool chained_simple_if_return(int i) {{{$}}
+// CHECK-FIXES: {{^}} if (i < 5){{$}}
+// CHECK-FIXES: {{^ return true;$}}
+// CHECK-FIXES: {{^ return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool chained_simple_if_return_negated(int i) {
+ if (i < 5)
+ return false;
+ if (i > 10)
+ return false;
+ return true;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool chained_simple_if_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^}} if (i < 5){{$}}
+// CHECK-FIXES: {{^ return false;$}}
+// CHECK-FIXES: {{^ return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_chained_if_return_return(int i) {
+ if (i < 5) {
+ return true;
+ }
+ if (i > 10) {
+ return true;
+ }
+ return false;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_chained_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^}} if (i < 5) {{{$}}
+// CHECK-FIXES: {{^}} return true;{{$}}
+// CHECK-FIXES: {{^}} }{{$}}
+// CHECK-FIXES: {{^ return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_chained_if_return_return_negated(int i) {
+ if (i < 5) {
+ return false;
+ }
+ if (i > 10) {
+ return false;
+ }
+ return true;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_chained_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^}} if (i < 5) {{{$}}
+// CHECK-FIXES: {{^}} return false;{{$}}
+// CHECK-FIXES: {{^}} }{{$}}
+// CHECK-FIXES: {{^ return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t
+
+bool a1 = false;
+
+//=-=-=-=-=-=-= operator ==
+bool aa = false == a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant boolean literal supplied to boolean operator [readability-simplify-boolean-expr]
+// CHECK-FIXES: {{^bool aa = !a1;$}}
+bool ab = true == a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ab = a1;$}}
+bool a2 = a1 == false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a2 = !a1;$}}
+bool a3 = a1 == true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a3 = a1;$}}
+
+//=-=-=-=-=-=-= operator !=
+bool n1 = a1 != false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n1 = a1;$}}
+bool n2 = a1 != true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n2 = !a1;$}}
+bool n3 = false != a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n3 = a1;$}}
+bool n4 = true != a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n4 = !a1;$}}
+
+//=-=-=-=-=-=-= operator ||
+bool a4 = a1 || false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a4 = a1;$}}
+bool a5 = a1 || true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a5 = true;$}}
+bool a6 = false || a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a6 = a1;$}}
+bool a7 = true || a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a7 = true;$}}
+
+//=-=-=-=-=-=-= operator &&
+bool a8 = a1 && false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a8 = false;$}}
+bool a9 = a1 && true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a9 = a1;$}}
+bool ac = false && a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ac = false;$}}
+bool ad = true && a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ad = a1;$}}
+
+void if_with_bool_literal_condition() {
+ int i = 0;
+ if (false) {
+ i = 1;
+ } else {
+ i = 2;
+ }
+ i = 3;
+ // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: {{.*}} in if statement condition
+ // CHECK-FIXES: {{^ int i = 0;$}}
+ // CHECK-FIXES-NEXT: {{^ {$}}
+ // CHECK-FIXES-NEXT: {{^ i = 2;$}}
+ // CHECK-FIXES-NEXT: {{^ }$}}
+ // CHECK-FIXES-NEXT: {{^ i = 3;$}}
+
+ i = 4;
+ if (true) {
+ i = 5;
+ } else {
+ i = 6;
+ }
+ i = 7;
+ // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: {{.*}} in if statement condition
+ // CHECK-FIXES: {{^ i = 4;$}}
+ // CHECK-FIXES-NEXT: {{^ {$}}
+ // CHECK-FIXES-NEXT: {{^ i = 5;$}}
+ // CHECK-FIXES-NEXT: {{^ }$}}
+ // CHECK-FIXES-NEXT: {{^ i = 7;$}}
+
+ i = 8;
+ if (false) {
+ i = 9;
+ }
+ i = 11;
+ // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: {{.*}} in if statement condition
+ // CHECK-FIXES: {{^ i = 8;$}}
+ // CHECK-FIXES-NEXT: {{^ $}}
+ // CHECK-FIXES-NEXT: {{^ i = 11;$}}
+}
+
+void operator_equals() {
+ int i = 0;
+ bool b1 = (i > 2);
+ if (b1 == true) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(b1\) {$}}
+ i = 5;
+ } else {
+ i = 6;
+ }
+ bool b2 = (i > 4);
+ if (b2 == false) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(!b2\) {$}}
+ i = 7;
+ } else {
+ i = 9;
+ }
+ bool b3 = (i > 6);
+ if (true == b3) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(b3\) {$}}
+ i = 10;
+ } else {
+ i = 11;
+ }
+ bool b4 = (i > 8);
+ if (false == b4) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(!b4\) {$}}
+ i = 12;
+ } else {
+ i = 13;
+ }
+}
+
+void operator_or() {
+ int i = 0;
+ bool b5 = (i > 10);
+ if (b5 || false) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(b5\) {$}}
+ i = 14;
+ } else {
+ i = 15;
+ }
+ bool b6 = (i > 10);
+ if (b6 || true) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(true\) {$}}
+ i = 16;
+ } else {
+ i = 17;
+ }
+ bool b7 = (i > 10);
+ if (false || b7) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(b7\) {$}}
+ i = 18;
+ } else {
+ i = 19;
+ }
+ bool b8 = (i > 10);
+ if (true || b8) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(true\) {$}}
+ i = 20;
+ } else {
+ i = 21;
+ }
+}
+
+void operator_and() {
+ int i = 0;
+ bool b9 = (i > 20);
+ if (b9 && false) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(false\) {$}}
+ i = 22;
+ } else {
+ i = 23;
+ }
+ bool ba = (i > 20);
+ if (ba && true) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(ba\) {$}}
+ i = 24;
+ } else {
+ i = 25;
+ }
+ bool bb = (i > 20);
+ if (false && bb) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(false\) {$}}
+ i = 26;
+ } else {
+ i = 27;
+ }
+ bool bc = (i > 20);
+ if (true && bc) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(bc\) {$}}
+ i = 28;
+ } else {
+ i = 29;
+ }
+}
+
+void ternary_operator() {
+ int i = 0;
+ bool bd = (i > 20) ? true : false;
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in ternary expression result
+ // CHECK-FIXES: {{^ bool bd = i > 20;$}}
+
+ bool be = (i > 20) ? false : true;
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in ternary expression result
+ // CHECK-FIXES: {{^ bool be = i <= 20;$}}
+
+ bool bf = ((i > 20)) ? false : true;
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: {{.*}} in ternary expression result
+ // CHECK-FIXES: {{^ bool bf = i <= 20;$}}
+}
+
+void operator_not_equal() {
+ int i = 0;
+ bool bf = (i > 20);
+ if (false != bf) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(bf\) {$}}
+ i = 30;
+ } else {
+ i = 31;
+ }
+ bool bg = (i > 20);
+ if (true != bg) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(!bg\) {$}}
+ i = 32;
+ } else {
+ i = 33;
+ }
+ bool bh = (i > 20);
+ if (bh != false) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(bh\) {$}}
+ i = 34;
+ } else {
+ i = 35;
+ }
+ bool bi = (i > 20);
+ if (bi != true) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(!bi\) {$}}
+ i = 36;
+ } else {
+ i = 37;
+ }
+}
+
+void nested_booleans() {
+ if (false || (true || false)) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(false \|\| \(true\)\) {$}}
+ }
+ if (true && (true || false)) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(true && \(true\)\) {$}}
+ }
+ if (false || (true && false)) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(false \|\| \(false\)\) {$}}
+ }
+ if (true && (true && false)) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} to boolean operator
+ // CHECK-FIXES: {{^ if \(true && \(false\)\) {$}}
+ }
+}
+
+static constexpr bool truthy() {
+ return true;
+}
+
+#define HAS_XYZ_FEATURE true
+
+void macros_and_constexprs(int i = 0) {
+ bool b = (i == 1);
+ if (b && truthy()) {
+ // leave this alone; if you want it simplified, then you should
+ // inline the constexpr function first.
+ i = 1;
+ }
+ i = 2;
+ if (b && HAS_XYZ_FEATURE) {
+ // leave this alone; if you want it simplified, then you should
+ // inline the macro first.
+ i = 3;
+ }
+ i = 4;
+}
+
+bool conditional_return_statements(int i) {
+ if (i == 0) return true; else return false;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}} in conditional return statement
+// CHECK-FIXES: {{^}} return i == 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_return_statements_then_expr(int i, int j) {
+ if (i == j) return (i == 0); else return false;
+}
+
+bool conditional_return_statements_else_expr(int i, int j) {
+ if (i == j) return true; else return (i == 0);
+}
+
+bool negated_conditional_return_statements(int i) {
+ if (i == 0) return false; else return true;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}} in conditional return statement
+// CHECK-FIXES: {{^}} return i != 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool negative_condition_conditional_return_statement(int i) {
+ if (!(i == 0)) return false; else return true;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:25: warning: {{.*}} in conditional return statement
+// CHECK-FIXES: {{^}} return i == 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_compound_return_statements(int i) {
+ if (i == 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return statement
+// CHECK-FIXES: {{^}}bool conditional_compound_return_statements(int i) {{{$}}
+// CHECK-FIXES-NEXT: {{^}} return i == 1;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool negated_conditional_compound_return_statements(int i) {
+ if (i == 1) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return statement
+// CHECK-FIXES: {{^}}bool negated_conditional_compound_return_statements(int i) {{{$}}
+// CHECK-FIXES-NEXT: {{^}} return i != 1;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_return_statements_side_effects_then(int i) {
+ if (i == 2) {
+ macros_and_constexprs();
+ return true;
+ } else
+ return false;
+}
+
+bool negated_conditional_return_statements_side_effects_then(int i) {
+ if (i == 2) {
+ macros_and_constexprs();
+ return false;
+ } else
+ return true;
+}
+
+bool conditional_return_statements_side_effects_else(int i) {
+ if (i == 2)
+ return true;
+ else {
+ macros_and_constexprs();
+ return false;
+ }
+}
+
+bool negated_conditional_return_statements_side_effects_else(int i) {
+ if (i == 2)
+ return false;
+ else {
+ macros_and_constexprs();
+ return true;
+ }
+}
+
+void lambda_conditional_return_statements() {
+ auto lambda = [](int n) -> bool { if (n > 0) return true; else return false; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:55: warning: {{.*}} in conditional return statement
+ // CHECK-FIXES: {{^}} auto lambda = [](int n) -> bool { return n > 0; };{{$}}
+
+ auto lambda2 = [](int n) -> bool {
+ if (n > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ };
+ // CHECK-MESSAGES: :[[@LINE-5]]:16: warning: {{.*}} in conditional return statement
+ // CHECK-FIXES: {{^}} auto lambda2 = [](int n) -> bool {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} return n > 0;{{$}}
+ // CHECK-FIXES-NEXT: {{^}} };{{$}}
+
+ auto lambda3 = [](int n) -> bool { if (n > 0) {macros_and_constexprs(); return true; } else return false; };
+
+ auto lambda4 = [](int n) -> bool {
+ if (n > 0)
+ return true;
+ else {
+ macros_and_constexprs();
+ return false;
+ }
+ };
+
+ auto lambda5 = [](int n) -> bool { if (n > 0) return false; else return true; };
+ // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: {{.*}} in conditional return statement
+ // CHECK-FIXES: {{^}} auto lambda5 = [](int n) -> bool { return n <= 0; };{{$}}
+
+ auto lambda6 = [](int n) -> bool {
+ if (n > 0) {
+ return false;
+ } else {
+ return true;
+ }
+ };
+ // CHECK-MESSAGES: :[[@LINE-5]]:16: warning: {{.*}} in conditional return statement
+ // CHECK-FIXES: {{^}} auto lambda6 = [](int n) -> bool {{{$}}
+ // CHECK-FIXES-NEXT: {{^}} return n <= 0;{{$}}
+ // CHECK-FIXES-NEXT: {{^}} };{{$}}
+}
+
+void simple_conditional_assignment_statements(int i) {
+ bool b;
+ if (i > 10)
+ b = true;
+ else
+ b = false;
+ bool bb = false;
+ // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: {{.*}} in conditional assignment
+ // CHECK-FIXES: bool b;
+ // CHECK-FIXES: {{^ }}b = i > 10;{{$}}
+ // CHECK-FIXES: bool bb = false;
+
+ bool c;
+ if (i > 20)
+ c = false;
+ else
+ c = true;
+ bool c2 = false;
+ // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: {{.*}} in conditional assignment
+ // CHECK-FIXES: bool c;
+ // CHECK-FIXES: {{^ }}c = i <= 20;{{$}}
+ // CHECK-FIXES: bool c2 = false;
+
+ // Unchanged: different variables.
+ bool b2;
+ if (i > 12)
+ b = true;
+ else
+ b2 = false;
+
+ // Unchanged: no else statement.
+ bool b3;
+ if (i > 15)
+ b3 = true;
+
+ // Unchanged: not boolean assignment.
+ int j;
+ if (i > 17)
+ j = 10;
+ else
+ j = 20;
+
+ // Unchanged: different variables assigned.
+ int k = 0;
+ bool b4 = false;
+ if (i > 10)
+ b4 = true;
+ else
+ k = 10;
+}
+
+void complex_conditional_assignment_statements(int i) {
+ bool d;
+ if (i > 30) {
+ d = true;
+ } else {
+ d = false;
+ }
+ d = false;
+ // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+ // CHECK-FIXES: bool d;
+ // CHECK-FIXES: {{^ }}d = i > 30;{{$}}
+ // CHECK-FIXES: d = false;
+
+ bool e;
+ if (i > 40) {
+ e = false;
+ } else {
+ e = true;
+ }
+ e = false;
+ // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+ // CHECK-FIXES: bool e;
+ // CHECK-FIXES: {{^ }}e = i <= 40;{{$}}
+ // CHECK-FIXES: e = false;
+
+ // Unchanged: no else statement.
+ bool b3;
+ if (i > 15) {
+ b3 = true;
+ }
+
+ // Unchanged: not a boolean assignment.
+ int j;
+ if (i > 17) {
+ j = 10;
+ } else {
+ j = 20;
+ }
+
+ // Unchanged: multiple statements.
+ bool f;
+ if (j > 10) {
+ j = 10;
+ f = true;
+ } else {
+ j = 20;
+ f = false;
+ }
+
+ // Unchanged: multiple statements.
+ bool g;
+ if (j > 10)
+ f = true;
+ else {
+ j = 20;
+ f = false;
+ }
+
+ // Unchanged: multiple statements.
+ bool h;
+ if (j > 10) {
+ j = 10;
+ f = true;
+ } else
+ f = false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_conditional_compound_return(int i) {
+ if (i < 0) {
+ return true;
+ } else if (i < 10) {
+ return false;
+ } else if (i > 20) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_conditional_return(int i) {
+ if (i < 0)
+ return true;
+ else if (i < 10)
+ return false;
+ else if (i > 20)
+ return true;
+ else
+ return false;
+}
+
+// Unchanged: chained assignments, but ChainedConditionalAssignment not set.
+void chained_conditional_compound_assignment(int i) {
+ bool b;
+ if (i < 0) {
+ b = true;
+ } else if (i < 10) {
+ b = false;
+ } else if (i > 20) {
+ b = true;
+ } else {
+ b = false;
+ }
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+void chained_conditional_assignment(int i) {
+ bool b;
+ if (i < 0)
+ b = true;
+ else if (i < 10)
+ b = false;
+ else if (i > 20)
+ b = true;
+ else
+ b = false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_simple_if_return_negated(int i) {
+ if (i < 5)
+ return false;
+ if (i > 10)
+ return false;
+ return true;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool complex_chained_if_return_return(int i) {
+ if (i < 5) {
+ return true;
+ }
+ if (i > 10) {
+ return true;
+ }
+ return false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool complex_chained_if_return_return_negated(int i) {
+ if (i < 5) {
+ return false;
+ }
+ if (i > 10) {
+ return false;
+ }
+ return true;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_simple_if_return(int i) {
+ if (i < 5)
+ return true;
+ if (i > 10)
+ return true;
+ return false;
+}
+
+bool simple_if_return_return(int i) {
+ if (i > 10)
+ return true;
+ return false;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool simple_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^ return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool simple_if_return_return_negated(int i) {
+ if (i > 10)
+ return false;
+ return true;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool simple_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^ return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_if_return_return(int i) {
+ if (i > 10) {
+ return true;
+ }
+ return false;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^ return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_if_return_return_negated(int i) {
+ if (i > 10) {
+ return false;
+ }
+ return true;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^ return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool if_implicit_bool_expr(int i) {
+ if (i & 1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return (i & 1) != 0;{{$}}
+
+bool negated_if_implicit_bool_expr(int i) {
+ if (i - 1) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return (i - 1) == 0;{{$}}
+
+bool implicit_int(int i) {
+ if (i) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return i != 0;{{$}}
+
+bool explicit_bool(bool b) {
+ if (b) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return b;{{$}}
+
+class Implicit {
+public:
+ operator bool() {
+ return true;
+ }
+};
+
+bool object_bool_implicit_conversion(Implicit O) {
+ if (O) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return O;{{$}}
+
+bool negated_explicit_bool(bool b) {
+ if (!b) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return !b;{{$}}
+
+bool bitwise_complement_conversion(int i) {
+ if (~i) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return ~i != 0;{{$}}
+
+bool logical_or(bool a, bool b) {
+ if (a || b) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return a || b;{{$}}
+
+bool logical_and(bool a, bool b) {
+ if (a && b) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return a && b;{{$}}
+
+class Comparable
+{
+public:
+ bool operator==(Comparable const &rhs) { return true; }
+ bool operator!=(Comparable const &rhs) { return false; }
+};
+
+bool comparable_objects() {
+ Comparable c;
+ Comparable d;
+ if (c == d) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return c == d;{{$}}
+
+bool negated_comparable_objects() {
+ Comparable c;
+ Comparable d;
+ if (c == d) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return c != d;{{$}}
+
+struct X {
+ explicit operator bool();
+};
+
+void explicit_conversion_assignment(X x) {
+ bool y;
+ if (x) {
+ y = true;
+ } else {
+ y = false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+// CHECK-FIXES: {{^ bool y;$}}
+// CHECK-FIXES: {{^}} y = static_cast<bool>(x);{{$}}
+
+void ternary_integer_condition(int i) {
+ bool b = i ? true : false;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:16: warning: {{.*}} in ternary expression result
+// CHECK-FIXES: bool b = i != 0;{{$}}
+
+bool non_null_pointer_condition(int *p1) {
+ if (p1) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p1 != nullptr;{{$}}
+
+bool null_pointer_condition(int *p2) {
+ if (!p2) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p2 == nullptr;{{$}}
+
+bool negated_non_null_pointer_condition(int *p3) {
+ if (p3) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p3 == nullptr;{{$}}
+
+bool negated_null_pointer_condition(int *p4) {
+ if (!p4) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p4 != nullptr;{{$}}
+
+bool comments_in_the_middle(bool b) {
+ if (b) {
+ return true;
+ } else {
+ // something wicked this way comes
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} if (b) {
+// CHECK-FIXES: // something wicked this way comes{{$}}
+
+bool preprocessor_in_the_middle(bool b) {
+ if (b) {
+ return true;
+ } else {
+#define SOMETHING_WICKED false
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} if (b) {
+// CHECK-FIXES: {{^}}#define SOMETHING_WICKED false
+
+bool integer_not_zero(int i) {
+ if (i) {
+ return false;
+ } else {
+ return true;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}} return i == 0;{{$}}
+
+class A {
+public:
+ int m;
+};
+
+bool member_pointer_nullptr(int A::*p) {
+ if (p) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p != nullptr;{{$}}
+
+bool integer_member_implicit_cast(A *p) {
+ if (p->m) {
+ return true;
+ } else {
+ return false;
+ }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p->m != 0;{{$}}
--- /dev/null
+// RUN: %check_clang_tidy %s readability-static-definition-in-anonymous-namespace %t
+
+namespace {
+
+int a = 1;
+const int b = 1;
+static int c = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'c' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace]
+// CHECK-FIXES: {{^}}int c = 1;
+static const int d = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'd' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}const int d = 1;
+const static int e = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'e' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}const int e = 1;
+
+void f() {
+ int a = 1;
+ static int b = 1;
+}
+
+static int g() {
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'g' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}int g() {
+ return 1;
+}
+
+#define DEFINE_STATIC static
+// CHECK-FIXES: {{^}}#define DEFINE_STATIC static
+DEFINE_STATIC int h = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'h' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}DEFINE_STATIC int h = 1;
+
+#define DEFINE_STATIC_VAR(x) static int x = 2
+// CHECK-FIXES: {{^}}#define DEFINE_STATIC_VAR(x) static int x = 2
+DEFINE_STATIC_VAR(i);
+// CHECK-FIXES: {{^}}DEFINE_STATIC_VAR(i);
+
+} // namespace
+
+namespace N {
+
+int a = 1;
+const int b = 1;
+static int c = 1;
+static const int d = 1;
+const static int e = 1;
+
+} // namespace N
--- /dev/null
+// RUN: %check_clang_tidy %s readability-uniqueptr-delete-release %t
+
+namespace std {
+template <typename T>
+struct default_delete {};
+
+template <typename T, typename D = default_delete<T>>
+class unique_ptr {
+ public:
+ unique_ptr();
+ ~unique_ptr();
+ explicit unique_ptr(T*);
+ template <typename U, typename E>
+ unique_ptr(unique_ptr<U, E>&&);
+ T* release();
+};
+} // namespace std
+
+std::unique_ptr<int>& ReturnsAUnique();
+
+void Positives() {
+ std::unique_ptr<int> P;
+ delete P.release();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> objects [readability-uniqueptr-delete-release]
+ // CHECK-FIXES: {{^}} P = nullptr;
+
+ std::unique_ptr<int> Array[20];
+ delete Array[4].release();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete
+ // CHECK-FIXES: {{^}} Array[4] = nullptr;
+
+ delete ReturnsAUnique().release();
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete
+ // CHECK-FIXES: {{^}} ReturnsAUnique() = nullptr;
+}
+
+struct NotDefaultDeleter {};
+
+struct NotUniquePtr {
+ int* release();
+};
+
+void Negatives() {
+ std::unique_ptr<int, NotDefaultDeleter> P;
+ delete P.release();
+
+ NotUniquePtr P2;
+ delete P2.release();
+}
+
+template <typename T, typename D>
+void NegativeDeleterT() {
+ // Ideally this would trigger a warning, but we have all dependent types
+ // disabled for now.
+ std::unique_ptr<T> P;
+ delete P.release();
+
+ // We ignore this one because the deleter is a template argument.
+ // Not all instantiations will use the default deleter.
+ std::unique_ptr<int, D> P2;
+ delete P2.release();
+}
+template void NegativeDeleterT<int, std::default_delete<int>>();
+
+// Test some macros
+
+#define DELETE_RELEASE(x) delete (x).release()
+void NegativesWithTemplate() {
+ std::unique_ptr<int> P;
+ DELETE_RELEASE(P);
+}
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-*' -- 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy %s -checks='-*,an-unknown-check' -- 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' -check-prefix=CHECK2 %s
+
+// CHECK2: Error: no checks enabled.
+
+namespace i {
+}
+// CHECK: :[[@LINE-1]]:2: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
+
+// Expect no warnings from the google-explicit-constructor check:
+class A { A(int i); };
--- /dev/null
+// RUN: clang-tidy -checks=-*,llvm-namespace-comment %s -- -serialize-diagnostics %t | FileCheck %s
+// CHECK: :[[@LINE+1]]:12: error: expected ';' after struct [clang-diagnostic-error]
+struct A {}
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,clang-analyzer-unix.Malloc' -config='{CheckOptions: [{ key: "clang-analyzer-unix.Malloc:Optimistic", value: true}]}' -- | FileCheck %s
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void free(void *);
+void __attribute((ownership_returns(malloc))) *my_malloc(size_t);
+void __attribute((ownership_takes(malloc, 1))) my_free(void *);
+
+void f1() {
+ void *p = malloc(12);
+ return;
+ // CHECK: warning: Potential leak of memory pointed to by 'p' [clang-analyzer-unix.Malloc]
+}
+
+void af2() {
+ void *p = my_malloc(12);
+ my_free(p);
+ free(p);
+ // CHECK: warning: Attempt to free released memory [clang-analyzer-unix.Malloc]
+}
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,clang-analyzer-*' -- | FileCheck %s
+extern void *malloc(unsigned long);
+extern void free(void *);
+
+void f() {
+ int *p = new int(42);
+ delete p;
+ delete p;
+ // CHECK: warning: Attempt to free released memory [clang-analyzer-cplusplus.NewDelete]
+}
+
+void g() {
+ void *q = malloc(132);
+ free(q);
+ free(q);
+ // CHECK: warning: Attempt to free released memory [clang-analyzer-unix.Malloc]
+}
--- /dev/null
+// RUN: clang-tidy -checks='-*,clang-analyzer-core.NullDereference' -analyze-temporary-dtors %s -- | FileCheck %s
+
+struct NoReturnDtor {
+ ~NoReturnDtor() __attribute__((noreturn));
+};
+
+extern bool check(const NoReturnDtor &);
+
+// CHECK-NOT: warning
+void testNullPointerDereferencePositive() {
+ int *value = 0;
+ // CHECK: [[@LINE+1]]:10: warning: Dereference of null pointer (loaded from variable 'value') [clang-analyzer-core.NullDereference]
+ *value = 1;
+}
+
+// CHECK-NOT: warning
+void testNullPointerDereference() {
+ int *value = 0;
+ if (check(NoReturnDtor())) {
+ // This unreachable code causes a warning if we don't run with -analyze-temporary-dtors
+ *value = 1;
+ }
+}
--- /dev/null
+// Check names may only contain alphanumeric characters, '-', '_', and '.'.
+// RUN: clang-tidy -checks=* -list-checks | grep '^ ' | cut -b5- | not grep -v '^[a-zA-Z0-9_.\-]\+$'
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN: -- -Wunused-variable 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN: -warnings-as-errors='clang-diagnostic*' -- -Wunused-variable 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN: -warnings-as-errors='clang-diagnostic*' -quiet -- -Wunused-variable 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+void f() { int i; }
+// CHECK-WARN: warning: unused variable 'i' [clang-diagnostic-unused-variable]
+// CHECK-WERR: error: unused variable 'i' [clang-diagnostic-unused-variable,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: unused variable 'i' [clang-diagnostic-unused-variable,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
+// CHECK-WERR-QUIET-NOT: treated as
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' -- 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN: -warnings-as-errors='llvm-namespace-comment' -- 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN: -warnings-as-errors='llvm-namespace-comment' -quiet -- 2>&1 \
+// RUN: | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+namespace j {
+}
+// CHECK-WARN: warning: namespace 'j' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'j' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'j' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+namespace k {
+}
+// CHECK-WARN: warning: namespace 'k' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'k' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'k' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 2 warnings treated as errors
+// CHECK-WERR-QUIET-NOT: treated as
--- /dev/null
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment' -warnings-as-errors='llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment' -warnings-as-errors='llvm-namespace-comment' -quiet -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+namespace i {
+}
+// CHECK-WARN: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'i' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'i' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
+// CHECK-WERR-QUIET-NOT: treated as
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+# Test authority-less URI\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 246\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n fake f;\n f.\n}\n"}}}\r
+\r
+Content-Length: 146\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}\r
+# Test authority-less URI\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK: ]}\r
+\r
+Content-Length: 172\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}}\r
+# Test params parsing in the presence of a 1.x-compatible client (inlined "uri")\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK: ]}\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 246\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n fake f;\n f.\n}\n"}}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# The order of results returned by ASTUnit CodeComplete seems to be\r
+# nondeterministic, so we check regardless of order.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"99964bb","filterText":"bb","insertText":"bb"}\r
+# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"99964ccc","filterText":"ccc","insertText":"ccc"}\r
+# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"99965operator=","filterText":"operator=","insertText":"operator="}\r
+# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"99965~fake","filterText":"~fake","insertText":"~fake"}\r
+# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"99964f","filterText":"f","insertText":"f"}\r
+# CHECK: ]}\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# Repeat the completion request, expect the same results.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"99964bb","filterText":"bb","insertText":"bb"}\r
+# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"99964ccc","filterText":"ccc","insertText":"ccc"}\r
+# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"99965operator=","filterText":"operator=","insertText":"operator="}\r
+# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"99965~fake","filterText":"~fake","insertText":"~fake"}\r
+# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"99964f","filterText":"f","insertText":"f"}\r
+# CHECK: ]}\r
+# Update the source file and check for completions again.\r
+Content-Length: 226\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct fancy { int (*func())(int, int); };\nint main() {\n fancy f;\n f.\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# Repeat the completion request, expect the same results.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":[\r
+# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int, int)","sortText":"99965func","filterText":"func","insertText":"func"}\r
+# CHECK: ]}\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":4,"method":"shutdown"}\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 172\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}\r
+# Go to local variable\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}\r
+# Go to local variable, end of token\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 214\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo bar = { x : 1 };\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}\r
+# Go to field, GNU old-style field designator \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 215\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo baz = { .x = 2 };\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}\r
+# Go to field, field designator \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 187\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n main();\n return 0;\n}"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}\r
+# Go to function declaration, function call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}\r
+\r
+Content-Length: 208\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n Foo bar;\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}\r
+# Go to struct declaration, new struct instance \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 1}}}]}\r
+\r
+Content-Length: 231\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n n1::Foo bar;\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}\r
+# Go to struct declaration, new struct instance, qualified name \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}\r
+\r
+Content-Length: 215\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n int x;\n};\nint main() {\n Foo bar;\n bar.x;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}\r
+# Go to field declaration, field reference \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 7}}}]}\r
+\r
+Content-Length: 220\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n void x();\n};\nint main() {\n Foo bar;\n bar.x();\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}\r
+# Go to method declaration, method call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 10}}}]}\r
+\r
+Content-Length: 240\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n TypedefFoo bar;\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}\r
+# Go to typedef \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 22}}}]}\r
+\r
+Content-Length: 254\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}\r
+# Go to template type parameter. Fails until clangIndex is modified to handle those.\r
+# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]}\r
+\r
+Content-Length: 256\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n ns::Foo::bar();\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}\r
+# Go to namespace, static method call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 1}}}]}\r
+\r
+Content-Length: 265\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n int field;\n Foo(int param) : field(param) {}\n};\n}\nint main() {\n return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}\r
+# Go to field, member initializer \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2, "character": 11}}}]}\r
+\r
+Content-Length: 204\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n return MY_MACRO;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}\r
+# Go to macro\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 18}}}]}\r
+\r
+Content-Length: 217\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}\r
+# Go to macro, re-defined later\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 13}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}\r
+# Go to macro, undefined later\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}\r
+# Go to macro, being undefined\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}\r
+\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 152\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"void main() {}"}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 1}, "end": {"line": 0, "character": 1}},"severity":2,"message":"return type of 'main' is not 'int'"},{"range":{"start": {"line": 0, "character": 1}, "end": {"line": 0, "character": 1}},"severity":3,"message":"change return type to 'int'"}]}}\r
+#\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 205\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"},"metadata":{"extraFlags":["-Wall"]}}}\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}}\r
+#\r
+Content-Length: 175\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":2},"contentChanges":[{"text":"int main() { int i; return i; }"}]}}\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}}\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
+\r
+\r
--- /dev/null
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 180\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}\r
+#\r
+Content-Length: 746\r
+\r
+ {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
--- /dev/null
+# RUN: clangd < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+# CHECK: Content-Length: 462\r
+# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{\r
+# CHECK: "textDocumentSync": 1,\r
+# CHECK: "documentFormattingProvider": true,\r
+# CHECK: "documentRangeFormattingProvider": true,\r
+# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},\r
+# CHECK: "codeActionProvider": true,\r
+# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},\r
+# CHECK: "definitionProvider": true\r
+# CHECK: }}}\r
+#\r
+Content-Length: 193\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int foo ( int x ) {\n x = x+1;\n return x;\n }"}}}\r
+#\r
+#\r
+Content-Length: 233\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/rangeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":1,"character":4},"end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 0, "character": 19}, "end": {"line": 1, "character": 4}}, "newText": "\n "},{"range": {"start": {"line": 1, "character": 9}, "end": {"line": 1, "character": 9}}, "newText": " "},{"range": {"start": {"line": 1, "character": 10}, "end": {"line": 1, "character": 10}}, "newText": " "},{"range": {"start": {"line": 1, "character": 12}, "end": {"line": 2, "character": 4}}, "newText": "\n "}]}\r
+#\r
+#\r
+Content-Length: 197\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n x = x + 1;\n return x;\n }"}]}}\r
+#\r
+#\r
+Content-Length: 233\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/rangeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":1,"character":2},"end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[]}\r
+#\r
+Content-Length: 153\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":[{"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range": {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}}, "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 16}}, "newText": ""},{"range": {"start": {"line": 2, "character": 11}, "end": {"line": 3, "character": 4}}, "newText": "\n"}]}\r
+#\r
+#\r
+Content-Length: 190\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":9},"contentChanges":[{"text":"int foo(int x) {\n x = x + 1;\n return x;\n}"}]}}\r
+#\r
+#\r
+Content-Length: 153\r
+\r
+{"jsonrpc":"2.0","id":4,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":[]}\r
+#\r
+Content-Length: 193\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n x = x + 1;\n return x;\n}"}]}}\r
+#\r
+#\r
+Content-Length: 204\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"position":{"line":3,"character":1},"ch":"}","options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":5,"result":[{"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range": {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}}, "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 16}}, "newText": ""}]}\r
+#\r
+\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":6,"method":"shutdown"}\r
--- /dev/null
+[
+{
+ "directory": "test_dir/build",
+ "command": "clang++ -I../include -o bar.o test_dir/src/bar.cpp",
+ "file": "test_dir/src/bar.cpp"
+}
+]
--- /dev/null
+---
+Name: foo
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+ - ContextType: Namespace
+ ContextName: b
+FilePath: foo.h
+Type: Class
+Seen: 1
+Used: 0
+---
+Name: foo_bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+ - ContextType: Namespace
+ ContextName: b
+FilePath: foobar.h
+Type: Class
+Seen: 0
+Used: 0
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+ - ContextType: Namespace
+ ContextName: b
+FilePath: ../include/bar.h
+Type: Class
+Seen: 1
+Used: 0
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+ - ContextType: Namespace
+ ContextName: b
+FilePath: ../include/bar.h
+Type: Class
+Seen: 3
+Used: 0
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+ - ContextType: Namespace
+ ContextName: b
+FilePath: ../include/zbar.h
+Type: Class
+Seen: 3
+Used: 0
+---
+Name: b
+Contexts:
+FilePath: var.h
+Type: Variable
+Seen: 1
+Used: 0
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: c
+FilePath: test/include-fixer/baz.h
+Type: Class
+Seen: 1
+Used: 0
--- /dev/null
+---
+Name: foo
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: foo.h
+Type: Class
+Seen: 1
+Used: 1
+...
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: ../include/bar.h
+Type: Class
+Seen: 1
+Used: 2
+...
--- /dev/null
+---
+Name: foo
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: foo.h
+Type: Class
+Seen: 1
+Used: 2
+...
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: ../include/barbar.h
+Type: Class
+Seen: 1
+Used: 0
+...
--- /dev/null
+// RUN: echo "foo f;" > %t.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -output-headers %t.cpp -- | FileCheck %s
+// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "foo"}]}' %t.cpp | FileCheck %s -check-prefix=CHECK-CODE
+// RUN: cat %t.cpp | not clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "foo"},{Header: "\"foo2.h\"", QualifiedName: "foo"}]}' %t.cpp
+// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "a:foo"},{Header: "\"foo.h\"", QualifiedName: "b:foo"}]}' %t.cpp
+//
+// CHECK: "HeaderInfos": [
+// CHECK-NEXT: {"Header": "\"foo.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"},
+// CHECK-NEXT: {"Header": "\"bar.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"}
+// CHECK-NEXT:]
+//
+// CHECK-CODE: #include "foo.h"
+// CHECK-CODE: foo f;
--- /dev/null
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: not clang-include-fixer -db=fixed -input='foo= "foo.h"' %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK-NOT: #include
+// CHECK: #include "doesnotexist.h"
+// CHECK-NEXT: foo f;
+
+#include "doesnotexist.h"
+foo f;
--- /dev/null
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: foo f;
+
+foo f;
--- /dev/null
+// RUN: mkdir -p %T/include-fixer/include
+// RUN: mkdir -p %T/include-fixer/symbols
+// RUN: mkdir -p %T/include-fixer/build
+// RUN: mkdir -p %T/include-fixer/src
+// RUN: sed 's|test_dir|%/T/include-fixer|g' %S/Inputs/database_template.json > %T/include-fixer/build/compile_commands.json
+// RUN: echo -e '#include "bar.h"\nb::a::bar f;' > %T/include-fixer/src/bar.cpp
+// RUN: echo 'namespace b { namespace a { class bar {}; } }' > %T/include-fixer/include/bar.h
+// RUN: cd %T/include-fixer/build
+// RUN: find-all-symbols -output-dir=%T/include-fixer/symbols -p=. %T/include-fixer/src/bar.cpp
+// RUN: find-all-symbols -merge-dir=%T/include-fixer/symbols %T/include-fixer/build/find_all_symbols.yaml
+// RUN: FileCheck -input-file=%T/include-fixer/build/find_all_symbols.yaml -check-prefix=CHECK-YAML %s
+//
+// RUN: echo 'b::a::bar f;' > %T/include-fixer/src/bar.cpp
+// RUN: clang-include-fixer -db=yaml -input=%T/include-fixer/build/find_all_symbols.yaml -minimize-paths=true -p=. %T/include-fixer/src/bar.cpp
+// RUN: FileCheck -input-file=%T/include-fixer/src/bar.cpp %s
+
+// CHECK-YAML: ..{{[/\\]}}include{{[/\\]}}bar.h
+// CHECK: #include "bar.h"
+// CHECK: b::a::bar f;
--- /dev/null
+# RUN: find-all-symbols -merge-dir=%S/Inputs/merge %t.merged
+# RUN: sed '/^#/d' %s > %t.golden
+# RUN: diff -u %t.golden %t.merged
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: ../include/bar.h
+Type: Class
+Seen: 1
+Used: 1
+...
+---
+Name: bar
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: ../include/barbar.h
+Type: Class
+Seen: 1
+Used: 0
+...
+---
+Name: foo
+Contexts:
+ - ContextType: Namespace
+ ContextName: a
+FilePath: foo.h
+Type: Class
+Seen: 2
+Used: 2
+...
--- /dev/null
+// REQUIRES: shell
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: mkdir -p %T/include-fixer/multiple-fixes
+// RUN: echo 'foo f;' > %T/include-fixer/multiple-fixes/foo.cpp
+// RUN: echo 'bar b;' > %T/include-fixer/multiple-fixes/bar.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h";bar= "bar.h"' %T/include-fixer/multiple-fixes/*.cpp --
+// RUN: FileCheck -input-file=%T/include-fixer/multiple-fixes/bar.cpp %s -check-prefix=CHECK-BAR
+// RUN: FileCheck -input-file=%T/include-fixer/multiple-fixes/foo.cpp %s -check-prefix=CHECK-FOO
+//
+// CHECK-FOO: #include "foo.h"
+// CHECK-FOO: foo f;
+// CHECK-BAR: #include "bar.h"
+// CHECK-BAR: bar b;
--- /dev/null
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=yaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK-NOT: #include
+// CHECK: doesnotexist f;
+
+namespace b {
+doesnotexist f;
+}
--- /dev/null
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -query-symbol="foo" test.cpp -- | FileCheck %s
+
+// CHECK: "FilePath": "test.cpp",
+// CHECK-NEXT:"QuerySymbolInfos": [
+// CHECK-NEXT: {"RawIdentifier": "foo",
+// CHECK-NEXT: "Range":{"Offset":0,"Length":0}}
+// CHECK-NEXT:],
+// CHECK-NEXT:"HeaderInfos": [
+// CHECK-NEXT: {"Header": "\"foo.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"},
+// CHECK-NEXT: {"Header": "\"bar.h\"",
+// CHECK-NEXT: "QualifiedName": "foo"}
+// CHECK-NEXT:]
--- /dev/null
+// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s
+// RUN: clang-include-fixer -query-symbol bar -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s
+
+// CHECK: "HeaderInfos": [
+// CHECK-NEXT: {"Header": "\"test/include-fixer/baz.h\"",
+// CHECK-NEXT: "QualifiedName": "c::bar"},
+// CHECK-NEXT: {"Header": "\"../include/bar.h\"",
+// CHECK-NEXT: "QualifiedName": "b::a::bar"},
+// CHECK-NEXT: {"Header": "\"../include/zbar.h\"",
+// CHECK-NEXT: "QualifiedName": "b::a::bar"}
+// CHECK-NEXT:]
+
+bar b;
--- /dev/null
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=fuzzyYaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// include-fixer will add the include, but doesn't complete the symbol.
+// CHECK: #include "foobar.h"
+// CHECK: fba f;
+
+b::a::fba f;
--- /dev/null
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=yaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: b::a::foo f;
+
+b::a::foo f;
--- /dev/null
+// RUN: mkdir -p %T/foo/bar
+// RUN: cp %p/Inputs/fake_yaml_db.yaml %T/find_all_symbols_db.yaml
+// RUN: cd %T/foo
+// RUN: sed -e 's#//.*$##' %s > bar/test.cpp
+// RUN: clang-include-fixer -db=yaml bar/test.cpp --
+// RUN: FileCheck %s -input-file=bar/test.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: b::a::foo f;
+
+b::a::foo f;
--- /dev/null
+// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-include-fixer -fspell-checking -Xclang -plugin-arg-clang-include-fixer -Xclang -input=%p/Inputs/fake_yaml_db.yaml 2>&1 | FileCheck %s
+
+foo f;
+foo g;
+unknown u;
+
+// CHECK: yamldb_plugin.cpp:3:1: error: unknown type name 'foo'; did you mean 'foo'?
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Replace [3:1 - 3:4] with "foo"
+// CHECK: yamldb_plugin.cpp:3:1: note: Add '#include "foo.h"' to provide the missing declaration [clang-include-fixer]
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Insert "#include "foo.h"
+// CHECK: yamldb_plugin.cpp:4:1: error: unknown type name 'foo'; did you mean 'foo'?
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Replace [4:1 - 4:4] with "foo"
+// CHECK: yamldb_plugin.cpp:4:1: note: Add '#include "foo.h"' to provide the missing declaration [clang-include-fixer]
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Insert "#include "foo.h"
+// CHECK: " at 3:1
+// CHECK: yamldb_plugin.cpp:5:1:
+// CHECK: error: unknown type name 'unknown'
+// CHECK: Number FIX-ITs = 0
+// CHECK-NOT: error
+// CHECK-NOT: FIX-IT
--- /dev/null
+# -*- Python -*-
+
+import os
+import platform
+import re
+import subprocess
+
+import lit.formats
+import lit.util
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'Clang Tools'
+
+# Tweak PATH for Win32
+if platform.system() == 'Windows':
+ # Seek sane tools in directories and set to $PATH.
+ path = getattr(config, 'lit_tools_dir', None)
+ path = lit_config.getToolsPath(path,
+ config.environment['PATH'],
+ ['cmp.exe', 'grep.exe', 'sed.exe'])
+ if path is not None:
+ path = os.path.pathsep.join((path,
+ config.environment['PATH']))
+ config.environment['PATH'] = path
+
+# Choose between lit's internal shell pipeline runner and a real shell. If
+# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
+use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL")
+if use_lit_shell:
+ # 0 is external, "" is default, and everything else is internal.
+ execute_external = (use_lit_shell == "0")
+else:
+ # Otherwise we default to internal on Windows and external elsewhere, as
+ # bash on Windows is usually very slow.
+ execute_external = (not sys.platform in ['win32'])
+
+# testFormat: The test format to use to interpret tests.
+#
+# For now we require '&&' between commands, until they get globally killed and
+# the test runner updated.
+config.test_format = lit.formats.ShTest(execute_external)
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.c', '.cpp', '.hpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s',
+ '.modularize', '.module-map-checker', '.test']
+
+# Test-time dependencies located in directories called 'Inputs' are excluded
+# from test suites; there won't be any lit tests within them.
+config.excludes = ['Inputs']
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+# test_exec_root: The root path where tests should be run.
+clang_tools_binary_dir = getattr(config, 'clang_tools_binary_dir', None)
+if clang_tools_binary_dir is not None:
+ config.test_exec_root = os.path.join(clang_tools_binary_dir, 'test')
+
+# Clear some environment variables that might affect Clang.
+#
+# This first set of vars are read by Clang, but shouldn't affect tests
+# that aren't specifically looking for these features, or are required
+# simply to run the tests at all.
+#
+# FIXME: Should we have a tool that enforces this?
+
+# safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
+# 'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
+# 'IOS_SIMULATOR_DEPLOYMENT_TARGET',
+# 'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
+# 'VC80COMNTOOLS')
+possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
+ 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
+ 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
+ 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
+ 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
+ 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
+ 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
+ 'LIBCLANG_RESOURCE_USAGE',
+ 'LIBCLANG_CODE_COMPLETION_LOGGING']
+# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
+if platform.system() != 'Windows':
+ possibly_dangerous_env_vars.append('INCLUDE')
+for name in possibly_dangerous_env_vars:
+ if name in config.environment:
+ del config.environment[name]
+
+# Tweak the PATH to include the tools dir and the scripts dir.
+if clang_tools_binary_dir is not None:
+ clang_tools_dir = getattr(config, 'clang_tools_dir', None)
+ if not clang_tools_dir:
+ lit_config.fatal('No Clang tools dir set!')
+ llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+ if not llvm_tools_dir:
+ lit_config.fatal('No LLVM tools dir set!')
+ path = os.path.pathsep.join((
+ clang_tools_dir, llvm_tools_dir, config.environment['PATH']))
+ config.environment['PATH'] = path
+
+ llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
+ if not llvm_libs_dir:
+ lit_config.fatal('No LLVM libs dir set!')
+ path = os.path.pathsep.join((llvm_libs_dir,
+ config.environment.get('LD_LIBRARY_PATH','')))
+ config.environment['LD_LIBRARY_PATH'] = path
+
+###
+
+# Check that the object root is known.
+if config.test_exec_root is None:
+ # Otherwise, we haven't loaded the site specific configuration (the user is
+ # probably trying to run on a test file directly, and either the site
+ # configuration hasn't been created by the build system, or we are in an
+ # out-of-tree build situation).
+
+ # Check for 'clang_site_config' user parameter, and use that if available.
+ site_cfg = lit_config.params.get('clang_tools_extra_site_config', None)
+ if site_cfg and os.path.exists(site_cfg):
+ lit_config.load_config(config, site_cfg)
+ raise SystemExit
+
+ # Try to detect the situation where we are using an out-of-tree build by
+ # looking for 'llvm-config'.
+ #
+ # FIXME: I debated (i.e., wrote and threw away) adding logic to
+ # automagically generate the lit.site.cfg if we are in some kind of fresh
+ # build situation. This means knowing how to invoke the build system though,
+ # and I decided it was too much magic. We should solve this by just having
+ # the .cfg files generated during the configuration step.
+
+ llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
+ if not llvm_config:
+ lit_config.fatal('No site specific configuration available!')
+
+ # Get the source and object roots.
+ llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
+ llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
+ clang_src_root = os.path.join(llvm_src_root, "tools", "clang")
+ clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang")
+
+ clang_tools_extra_src_root = os.path.join(clang_src_root, "tools", "extra")
+ clang_tools_extra_obj_root = os.path.join(clang_obj_root, "tools", "extra")
+ # Validate that we got a tree which points to here, using the standard
+ # tools/clang layout.
+ this_src_root = os.path.dirname(config.test_source_root)
+ if os.path.realpath(clang_tools_extra_src_root) != os.path.realpath(this_src_root):
+ lit_config.fatal('No site specific configuration available!')
+
+ # Check that the site specific configuration exists.
+ site_cfg = os.path.join(clang_tools_extra_obj_root, 'test', 'lit.site.cfg')
+ if not os.path.exists(site_cfg):
+ lit_config.fatal(
+ 'No site specific configuration available! You may need to '
+ 'run "make test" in your Clang build directory.')
+
+ # Okay, that worked. Notify the user of the automagic, and reconfigure.
+ lit_config.note('using out-of-tree build at %r' % clang_obj_root)
+ lit_config.load_config(config, site_cfg)
+ raise SystemExit
+
+###
+
+import os
+
+# When running under valgrind, we mangle '-vg' onto the end of the triple so we
+# can check it with XFAIL and XTARGET.
+if lit_config.useValgrind:
+ config.target_triple += '-vg'
+
+###
+
+# Set available features we allow tests to conditionalize on.
+#
+# As of 2011.08, crash-recovery tests still do not pass on FreeBSD.
+if platform.system() not in ['FreeBSD']:
+ config.available_features.add('crash-recovery')
+
+# Shell execution
+if execute_external:
+ config.available_features.add('shell')
+
+# Exclude MSYS due to transforming '/' to 'X:/mingwroot/'.
+if not platform.system() in ['Windows'] or not execute_external:
+ config.available_features.add('shell-preserves-root')
+
+# ANSI escape sequences in non-dumb terminal
+if platform.system() not in ['Windows']:
+ config.available_features.add('ansi-escape-sequences')
+
+check_clang_tidy = os.path.join(
+ config.test_source_root, "clang-tidy", "check_clang_tidy.py")
+config.substitutions.append(
+ ('%check_clang_tidy',
+ '%s %s' % (config.python_executable, check_clang_tidy)) )
+clang_tidy_diff = os.path.join(
+ config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py")
+config.substitutions.append(
+ ('%clang_tidy_diff',
+ '%s %s' % (config.python_executable, clang_tidy_diff)) )
--- /dev/null
+@LIT_SITE_CFG_IN_HEADER@
+
+import sys
+
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
+config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
+config.clang_tools_dir = "@CLANG_TOOLS_DIR@"
+config.python_executable = "@PYTHON_EXECUTABLE@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+# Support substitution of the tools and libs dirs with user parameters. This is
+# used when we can't determine the tool dir at configuration time.
+try:
+ config.clang_tools_dir = config.clang_tools_dir % lit_config.params
+ config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
+ config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params
+except KeyError:
+ e = sys.exc_info()[1]
+ key, = e.args
+ lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
+
+# Let the main config do the real work.
+lit_config.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/lit.cfg")
--- /dev/null
+// Exercise some anonymous type issues.
+
+// Anonymous enum.
+enum {
+ Tag1
+};
+
+// Anonymous enum typedef.
+typedef enum {
+ Tag2
+} AnonymousEnum;
--- /dev/null
+typedef WithoutDep BadType;
+
--- /dev/null
+#define MACRO_1A 1
--- /dev/null
+// module.map\r
+\r
+module Level1A {\r
+ header "Level1A.h"\r
+ export *\r
+}\r
+module HasError {\r
+ header "HasError.h"\r
+ export *\r
+}\r
--- /dev/null
+#error DontFindMe.h shouldn't be found.
+
+
--- /dev/null
+#define MACRO_1A 1
--- /dev/null
+#define MACRO_2A 1
--- /dev/null
+#define MACRO_3A 1
--- /dev/null
+// module.map
+
+module Level1A {
+ header "Includes1/Level1A.h"
+ export *
+}
+module Level2A {
+ header "Includes2/Level2A.h"
+ export *
+}
--- /dev/null
+#include "Level2A.h"
+#define MACRO_1A 1
--- /dev/null
+#include "Level2B.h"
+#define MACRO_1B 1
--- /dev/null
+#define MACRO_2A 1
--- /dev/null
+#define MACRO_2B 1
--- /dev/null
+#include "Sub/Level3B.h"
+#define MACRO_3A 1
--- /dev/null
+#define MACRO_3B 1
--- /dev/null
+#define MACRO_3B 1
--- /dev/null
+#define UMBRELLA_HEADER 1
+#include "UmbrellaInclude1.h"
+#include "UmbrellaInclude2.h"
--- /dev/null
+#define UMBRELLA_INCLUDE_1 1
--- /dev/null
+#define UMBRELLA_INCLUDE_2 1
--- /dev/null
+#define UMBRELLA_1 1
--- /dev/null
+#define UMBRELLA_2 1
--- /dev/null
+// module.map
+
+module Level1A {
+ header "Level1A.h"
+ export *
+}
+module Level1B {
+ header "Level1B.h"
+ export *
+ module Level2B {
+ header "Level2B.h"
+ export *
+ }
+}
+module Level2A {
+ header "Level2A.h"
+ export *
+}
+module UmbrellaDirectoryModule {
+ umbrella "UmbrellaSub"
+}
+module UmbrellaHeaderModule {
+ umbrella header "UmbrellaFile.h"
+}
+/*
+module NoHeader {
+ header "NoHeader.h"
+ export *
+}
+*/
--- /dev/null
+// Same decl as in DuplicateHeader2.h.
+typedef int TypeInt;
--- /dev/null
+// Same decl as in DuplicateHeader1.h.
+typedef int TypeInt;
--- /dev/null
+// Empty header for testing #include directives in blocks.
--- /dev/null
+#ifndef _HEADERGUARD_H_
+#define _HEADERGUARD_H_
+#include "HeaderGuardSub1.h"
+#include "HeaderGuardSub2.h"
+#include "HeaderGuardSubSub.h"
+#endif // _HEADERGUARD_H_
--- /dev/null
+#ifndef _HEADERGUARDSUB1_H_
+#define _HEADERGUARDSUB1_H_
+#include "HeaderGuardSubSub.h"
+#include "HeaderGuardSubSubDefined.h"
+#endif // _HEADERGUARDSUB1_H_
--- /dev/null
+#ifndef _HEADERGUARDSUB2_H_
+#define _HEADERGUARDSUB2_H_
+#include "HeaderGuardSubSub.h"
+#include "HeaderGuardSubSubDefined.h"
+#endif // _HEADERGUARDSUB2_H_
--- /dev/null
+#ifndef _HEADERGUARDSUBSUB_H_
+#define _HEADERGUARDSUBSUB_H_
+
+#define SOMETHING 1
+
+// Nest include. Header guard should not confuse modularize.
+#include "HeaderGuard.h"
+
+#endif // _HEADERGUARDSUBSUB_H_
--- /dev/null
+#if !defined(_HEADERGUARDSUBSUBDEFINED_H_)
+#define _HEADERGUARDSUBSUBDEFINED_H_
+
+#define SOMETHING_OTHER 1
+
+// Nest include. Header guard should not confuse modularize.
+#include "HeaderGuard.h"
+
+#endif // _HEADERGUARDSUBSUBDEFINED_H_
--- /dev/null
+extern "C" {
+ #include "Empty.h"
+}
--- /dev/null
+namespace MyNamespace {
+ #include "Empty.h"
+}
--- /dev/null
+// Define symbol such that a declaration exists when this header
+// is included, but not when InconsistentHeader2.h is included.
+#define SYMBOL1 1
+#include "InconsistentSubHeader.h"
--- /dev/null
+// Set up so the declaration in InconsistentSubHeader.h is not defined.
+#define SYMBOL2 1
+#include "InconsistentSubHeader.h"
--- /dev/null
+// Set up so TypeInt only defined during InconsistentHeader1.h include.
+#ifdef SYMBOL1
+#define SYMBOL 1
+#define FUNC_STYLE(a, b) a||b
+#endif
+#ifdef SYMBOL2
+#define SYMBOL 2
+#define FUNC_STYLE(a, b) a&&b
+#endif
+
+#if SYMBOL == 1
+typedef int TypeInt;
+#endif
+
+int var = FUNC_STYLE(1, 0);
+
+#if defined(SYMBOL1)
+#endif
--- /dev/null
+// This header depends on SomeTypes.h for the TypeInt typedef.
+
+typedef TypeInt NewTypeInt;
+typedef OtherTypeInt OtherNewTypeInt;
--- /dev/null
+#define MACRO_1A 1
--- /dev/null
+// module.map\r
+\r
+module Level1A {\r
+ header "Level1A.h"\r
+ export *\r
+}\r
+module Missing {\r
+ header "Missing.h"\r
+ export *\r
+}\r
--- /dev/null
+// Define same class name in different namespaces.
+
+namespace Namespace1 {
+ class NamespaceClass {
+ public:
+ NamespaceClass() : Member(0) {}
+ private:
+ int Member;
+ };
+}
+
+namespace Namespace2 {
+ class NamespaceClass {
+ public:
+ NamespaceClass() : Member(0) {}
+ private:
+ int Member;
+ };
+}
+
--- /dev/null
+// Verification of fix for nested macro.
+
+#define FUNCMACROINNER(a) a
+#define FUNCMACROOUTER(b, c) FUNCMACROINNER(b) + FUNCMACROINNER(c)
+int FuncMacroValue = FUNCMACROOUTER(1, 2);
--- /dev/null
+// NoProblems.modulemap
+module SomeTypes {
+ header "SomeTypes.h"
+ export *
+}
+module SomeDecls {
+ header "SomeDecls.h"
+ export *
+}
--- /dev/null
+// ProblemsDuplicate.modulemap
+module DuplicateHeader1 {
+ header "DuplicateHeader1.h"
+ export *
+}
+module DuplicateHeader2 {
+ header "DuplicateHeader2.h"
+ export *
+}
--- /dev/null
+// Declare a couple of functions - no modules problems.
+
+void FuncOne();
+
+int FuncTwo(int arg);
+
+void FuncOverload(int arg) {}
+void FuncOverload(char *arg) {}
+
+namespace Namespace1 {
+ void FuncNameSpace() {}
+}
+
+namespace Namespace2 {
+ void FuncNameSpace() {}
+}
--- /dev/null
+// Declare another type for the dependency check.
+// This file dependent on SomeTypes.h being included first.
+
+typedef TypeInt OtherTypeInt;
--- /dev/null
+// Define a few different kinds of types - no modules problems.
+
+typedef int TypeInt;
+
+typedef TypeInt NestedTypeInt;
+
+struct TypeStruct {
+ int Member;
+};
+
+class TypeClass {
+public:
+ TypeClass() : Member(0) {}
+private:
+ int Member;
+};
--- /dev/null
+// Header1.h - Empty.
--- /dev/null
+// Header2.h - Empty.
--- /dev/null
+// Header3.h - Empty.
--- /dev/null
+// Header4.h - Empty.
--- /dev/null
+// Exercise some template issues. Should not produce errors.
+
+// Forward declaration.
+template<class T> class TemplateClass;
+
+// Full declaration.
+template<class T>class TemplateClass {
+public:
+ TemplateClass() {}
+private:
+ T Member;
+};
+
+// Template alias.
+template<class T> using TemplateClassAlias = TemplateClass<T>;
--- /dev/null
+# RUN: modularize %s -x c++
+# RUN: modularize -prefix=%p %s -x c++
+# RUN: modularize -no-coverage-check %S/Inputs/NoProblems.modulemap -x c++
+
+Inputs/SomeTypes.h
+Inputs/SomeDecls.h
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/Anonymous.h
--- /dev/null
+# RUN: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix=%S/Input %s
+# RUN: FileCheck --input-file=%T/NoProblemsAssistant.txt %s
+
+SomeTypes.h
+SomeDecls.h
+SubModule1/Header1.h
+SubModule1/Header2.h
+SubModule2/Header3.h
+SubModule2/Header4.h
+SubModule2/Header5-dash.dot.h
+SubModule2.h
+
+# CHECK: // Output/NoProblemsAssistant.txt
+# CHECK-NEXT: // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix={{.*}}{{[/\\]}}{{.*}} {{.*}}{{[/\\]}}NoProblemsAssistant.modularize
+# CHECK: module Root {
+# CHECK-NEXT: module SomeTypes {
+# CHECK-NEXT: header "SomeTypes.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: module SomeDecls {
+# CHECK-NEXT: header "SomeDecls.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: module SubModule1 {
+# CHECK-NEXT: module Header1 {
+# CHECK-NEXT: header "SubModule1/Header1.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: module Header2 {
+# CHECK-NEXT: header "SubModule1/Header2.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: module SubModule2 {
+# CHECK-NEXT: module Header3 {
+# CHECK-NEXT: header "SubModule2/Header3.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: module Header4 {
+# CHECK-NEXT: header "SubModule2/Header4.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: module Header5_dash_dot {
+# CHECK-NEXT: header "SubModule2/Header5-dash.dot.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: header "SubModule2.h"
+# CHECK-NEXT: export *
+# CHECK-NEXT: }
+# CHECK-NEXT: }
--- /dev/null
+# RUN: modularize -I Includes1 -I Includes2 %S/Inputs/CoverageNoProblems/module.modulemap
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/IsDependent.h: Inputs/SomeTypes.h Inputs/SomeOtherTypes.h
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/HeaderGuardSub1.h
+Inputs/HeaderGuardSub2.h
+Inputs/HeaderGuard.h
--- /dev/null
+# RUN: modularize %S/NoProblems.modularize,%S/NoProblemsAnonymous.modularize -x c++
+# RUN: modularize -prefix=%p %S/NoProblems.modularize,%S/NoProblemsAnonymous.modularize -x c++
--- /dev/null
+# RUN: modularize -block-check-header-list-only
+
+Inputs/IncludeInNamespace.h
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/NamespaceClasses.h
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/NestedMacro.h
--- /dev/null
+# RUN: modularize %s -x c++
+
+Inputs/TemplateClasses.h
--- /dev/null
+# RUN: not modularize %S/Inputs/CompileError/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}CompileError{{[/\\]}}HasError.h:1:9: error: unknown type name 'WithoutDep'
--- /dev/null
+# RUN: not modularize %S/Inputs/CoverageProblems/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Level3A.h
+# CHECK-NEXT: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Level3B
+# CHECK-NEXT: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Sub/Level3B.h
--- /dev/null
+# RUN: not modularize -display-file-lists %S/Inputs/CompileError/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}CompileError{{[/\\]}}HasError.h:1:9: error: unknown type name 'WithoutDep'
+
+# CHECK: These are the files with possible errors:
+
+# CHECK: Inputs/CompileError/HasError.h
+
+# CHECK: These are the files with no detected errors:
+
+# CHECK: Inputs/CompileError/Level1A.h
+
+# CHECK: These are the combined files, with problem files preceded by #:
+
+# CHECK: {{.*}}Inputs/CompileError/HasError.h
+# CHECK: Inputs/CompileError/Level1A.h
--- /dev/null
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+# RUN: not modularize %S/Inputs/ProblemsDuplicate.modulemap -x c++ 2>&1 | FileCheck %s
+
+Inputs/DuplicateHeader1.h
+Inputs/DuplicateHeader2.h
+
+# CHECK: error: value 'TypeInt' defined at multiple locations:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}DuplicateHeader1.h:2:13
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}DuplicateHeader2.h:2:13
\ No newline at end of file
--- /dev/null
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInExtern.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:2:3:
+# CHECK-NEXT: #include "Empty.h"
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Include directive within extern "C" {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:1:1:
+# CHECK-NEXT: extern "C" {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "extern "C" {}" block is here.
--- /dev/null
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/InconsistentHeader1.h
+Inputs/InconsistentHeader2.h
+
+# CHECK: error: macro 'SYMBOL' defined at multiple locations:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9
+# CHECK-NEXT: error: macro 'FUNC_STYLE' defined at multiple locations:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:15:11:
+# CHECK-NEXT: int var = FUNC_STYLE(1, 0);
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Macro instance 'FUNC_STYLE(1, 0);' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1||0' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9:
+# CHECK-NEXT: #define FUNC_STYLE(a, b) a||b
+# CHECK-NEXT: ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1&&0' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9:
+# CHECK-NEXT: #define FUNC_STYLE(a, b) a&&b
+# CHECK-NEXT: ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:5:
+# CHECK-NEXT: #if SYMBOL == 1
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Macro instance 'SYMBOL' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9:
+# CHECK-NEXT: #define SYMBOL 1
+# CHECK-NEXT: ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9:
+# CHECK-NEXT: #define SYMBOL 2
+# CHECK-NEXT: ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:5:
+# CHECK-NEXT: #if defined(SYMBOL1)
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Macro instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h:3:9:
+# CHECK-NEXT: #define SYMBOL1 1
+# CHECK-NEXT: ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: (no macro definition)
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:2
+# CHECK-NEXT: #if SYMBOL == 1
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL == 1' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:2:2
+# CHECK-NEXT: #ifdef SYMBOL1
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL1' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'SYMBOL1' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: 'SYMBOL1' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:2
+# CHECK-NEXT: #ifdef SYMBOL2
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL2' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'SYMBOL2' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: 'SYMBOL2' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:2
+# CHECK-NEXT: #if defined(SYMBOL1)
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
+# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included.
+# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 3:9 not always provided
+# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 4:9 not always provided
+# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 7:9 not always provided
+# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 8:9 not always provided
+# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 12:13 not always provided
--- /dev/null
+# RUN: not modularize %S/Inputs/MissingHeader/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs/MissingHeader/module.modulemap:8:10: error : Header not found: Missing.h
--- /dev/null
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInNamespace.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:2:3:
+# CHECK-NEXT: #include "Empty.h"
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Include directive within namespace MyNamespace {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:1:1:
+# CHECK-NEXT: namespace MyNamespace {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "namespace MyNamespace {}" block is here.
--- /dev/null
+// SubModule2.h - Master header with same name as directory.
+#include "SubModule2/Header3.h"
+#include "SubModule2/Header4.h"
--- /dev/null
+#include "Level2A.h"
+#define MACRO_1A 1
--- /dev/null
+#include "Level2B.h"
+#define MACRO_1B 1
--- /dev/null
+#define MACRO_2A 1
--- /dev/null
+#define MACRO_2B 1
--- /dev/null
+Level1A.h
+Level1B.h
+Level2A.h
+Level2B.h
--- /dev/null
+// module.map\r
+\r
+module Level1A {\r
+ header "Level1A.h"\r
+ export *\r
+}\r
+module Level1B {\r
+ header "Level1B.h"\r
+ export *\r
+ module Level2B {\r
+ header "Level2B.h"\r
+ export *\r
+ }\r
+}\r
+module Level2A {\r
+ header "Level2A.h"\r
+ export *\r
+}\r
--- /dev/null
+// RUN: pp-trace -ignore FileChanged %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#if 1
+#endif
+
+#if 0
+#endif
+
+#if 1
+#else
+#endif
+
+#if 0
+#else
+#endif
+
+#if 1
+#elif 1
+#endif
+#if 1
+#elif 0
+#endif
+
+#if 0
+#elif 1
+#endif
+#if 0
+#elif 0
+#endif
+#if 1
+#elif 1
+#endif
+#if 1
+#elif 0
+#endif
+
+#if 0
+#elif 1
+#else
+#endif
+#if 0
+#elif 0
+#else
+#endif
+#if 1
+#elif 1
+#else
+#endif
+#if 1
+#elif 0
+#else
+#endif
+
+#define MACRO 1
+#ifdef MACRO
+#endif
+#ifdef NO_MACRO
+#endif
+#ifndef MACRO
+#endif
+#ifndef NO_MACRO
+#endif
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __cplusplus
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:4:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:4:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:11:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:11:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:15:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:26:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:26:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:2"]
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:40:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:40:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:44:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:47:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:48:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:48:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:51:1"]
+// CHECK-NEXT: ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:52:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:52:2"]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: Ifdef
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:55:2"
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:56:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:55:2"
+// CHECK-NEXT: - Callback: Ifdef
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2"
+// CHECK-NEXT: MacroNameTok: NO_MACRO
+// CHECK-NEXT: MacroDefinition: []
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:58:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:58:2"]
+// CHECK-NEXT: - Callback: Ifndef
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2"
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:60:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:60:2"]
+// CHECK-NEXT: - Callback: Ifndef
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:61:2"
+// CHECK-NEXT: MacroNameTok: NO_MACRO
+// CHECK-NEXT: MacroDefinition: []
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:62:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:61:2"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#ident "$Id$"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: Ident
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-ident.cpp:3:2"
+// CHECK-NEXT: Str: "$Id$"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#include "Inputs/Level1A.h"
+#include "Inputs/Level1B.h"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "<built-in>:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "<built-in>:1:1"
+// CHECK-NEXT: Reason: RenameFile
+// CHECK-NEXT: FileType: C_System
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __cplusplus
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK: - Callback: FileChanged
+// CHECK-NEXT: Loc: "<command line>:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "<built-in>:1:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (getFileEntryForID failed)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT: IncludeTok: include
+// CHECK-NEXT: FileName: "Inputs/Level1A.h"
+// CHECK-NEXT: IsAngled: false
+// CHECK-NEXT: FilenameRange: "Inputs/Level1A.h"
+// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Inputs/Level1A.h"
+// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}pp-trace"
+// CHECK-NEXT: RelativePath: "Inputs/Level1A.h"
+// CHECK-NEXT: Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level1A.h:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT: IncludeTok: include
+// CHECK-NEXT: FileName: "Level2A.h"
+// CHECK-NEXT: IsAngled: false
+// CHECK-NEXT: FilenameRange: "Level2A.h"
+// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Inputs/Level2A.h"
+// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}Inputs"
+// CHECK-NEXT: RelativePath: "Level2A.h"
+// CHECK-NEXT: Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level2A.h:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO_2A
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level1A.h:2:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Inputs/Level2A.h"
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO_1A
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:4:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Inputs/Level1A.h"
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT: IncludeTok: include
+// CHECK-NEXT: FileName: "Inputs/Level1B.h"
+// CHECK-NEXT: IsAngled: false
+// CHECK-NEXT: FilenameRange: "Inputs/Level1B.h"
+// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Inputs/Level1B.h"
+// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}pp-trace"
+// CHECK-NEXT: RelativePath: "Inputs/Level1B.h"
+// CHECK-NEXT: Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level1B.h:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT: IncludeTok: include
+// CHECK-NEXT: FileName: "Level2B.h"
+// CHECK-NEXT: IsAngled: false
+// CHECK-NEXT: FilenameRange: "Level2B.h"
+// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Inputs/Level2B.h"
+// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}Inputs"
+// CHECK-NEXT: RelativePath: "Level2B.h"
+// CHECK-NEXT: Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level2B.h:1:1"
+// CHECK-NEXT: Reason: EnterFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO_2B
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Inputs/Level1B.h:2:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Inputs/Level2B.h"
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO_1B
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:5:1"
+// CHECK-NEXT: Reason: ExitFile
+// CHECK-NEXT: FileType: C_User
+// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Inputs/Level1B.h"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace -ignore FileChanged %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#define MACRO 1
+int i = MACRO;
+#if defined(MACRO)
+#endif
+#undef MACRO
+#if defined(MACRO)
+#endif
+#define FUNCMACRO(ARG1) ARG1
+int j = FUNCMACRO(1);
+#define X X_IMPL(a+y,b) X_IMPL2(c)
+#define X_IMPL(p1,p2)
+#define X_IMPL2(p1)
+X
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __cplusplus
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK: MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9"]
+// CHECK-NEXT: Args: (null)
+// CHECK-NEXT: - Callback: Defined
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:19"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:1"]
+// CHECK-NEXT: ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2"
+// CHECK-NEXT: - Callback: MacroUndefined
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Defined
+// CHECK-NEXT: MacroNameTok: MACRO
+// CHECK-NEXT: MacroDefinition: []
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:19"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2"
+// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:1"]
+// CHECK-NEXT: ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2"
+// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2"]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: FUNCMACRO
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT: MacroNameTok: FUNCMACRO
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:20"]
+// CHECK-NEXT: Args: [1]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: X
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: X_IMPL
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT: MacroNameTok: X_IMPL2
+// CHECK-NEXT: MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT: MacroNameTok: X
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1"]
+// CHECK-NEXT: Args: (null)
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT: MacroNameTok: X_IMPL
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: [(nonfile), (nonfile)]
+// CHECK-NEXT: Args: [a <plus> y, b]
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT: MacroNameTok: X_IMPL2
+// CHECK-NEXT: MacroDefinition: [(local)]
+// CHECK-NEXT: Range: [(nonfile), (nonfile)]
+// CHECK-NEXT: Args: [c]
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: rm -rf %t
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -x objective-c++ -undef -target x86_64 -std=c++11 -fmodules -fcxx-modules -fmodules-cache-path=%t -I%S -I%S/Input | FileCheck --strict-whitespace %s
+
+// CHECK: ---
+
+@import Level1A;
+
+// CHECK-NEXT: - Callback: moduleImport
+// CHECK-NEXT: ImportLoc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-3]]:2"
+// CHECK-NEXT: Path: [{Name: Level1A, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:9"}]
+// CHECK-NEXT: Imported: Level1A
+
+@import Level1B.Level2B;
+
+// CHECK-NEXT: - Callback: moduleImport
+// CHECK-NEXT: ImportLoc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-3]]:2"
+// CHECK-NEXT: Path: [{Name: Level1B, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:9"}, {Name: Level2B, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:17"}]
+// CHECK-NEXT: Imported: Level2B
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s | FileCheck --strict-whitespace %s
+
+#pragma clang diagnostic push
+#pragma clang diagnostic pop
+#pragma clang diagnostic ignored "-Wformat"
+#pragma clang diagnostic warning "-Wformat"
+#pragma clang diagnostic error "-Wformat"
+#pragma clang diagnostic fatal "-Wformat"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic warning "-Wformat"
+#pragma GCC diagnostic error "-Wformat"
+#pragma GCC diagnostic fatal "-Wformat"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:3:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPush
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:3:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:4:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPop
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:4:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:5:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:5:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: Mapping: MAP_IGNORE
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:6:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:6:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: Mapping: MAP_WARNING
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:7:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:7:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: Mapping: MAP_ERROR
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:8:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:8:15"
+// CHECK-NEXT: Namespace: clang
+// CHECK-NEXT: Mapping: MAP_FATAL
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:10:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPush
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:10:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:11:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPop
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:11:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:12:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:12:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: Mapping: MAP_IGNORE
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:13:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:13:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: Mapping: MAP_WARNING
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:14:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:14:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: Mapping: MAP_ERROR
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:15:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:15:13"
+// CHECK-NEXT: Namespace: GCC
+// CHECK-NEXT: Mapping: MAP_FATAL
+// CHECK-NEXT: Str: -Wformat
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -target x86_64 -fms-extensions -w | FileCheck --strict-whitespace %s
+
+#pragma comment(compiler, "compiler comment")
+#pragma comment(exestr, "exestr comment")
+#pragma comment(lib, "lib comment")
+#pragma comment(linker, "linker comment")
+#pragma comment(user, "user comment")
+
+#pragma detect_mismatch("name argument", "value argument")
+
+#pragma __debug(assert)
+
+#pragma message("message argument")
+
+#pragma warning(push, 1)
+#pragma warning(pop)
+#pragma warning(disable : 1 2 3 ; error : 4 5 6 ; suppress : 7 8 9)
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:3:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:3:9"
+// CHECK-NEXT: Kind: compiler
+// CHECK-NEXT: Str: compiler comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:4:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:4:9"
+// CHECK-NEXT: Kind: exestr
+// CHECK-NEXT: Str: exestr comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:5:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:5:9"
+// CHECK-NEXT: Kind: lib
+// CHECK-NEXT: Str: lib comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:6:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:6:9"
+// CHECK-NEXT: Kind: linker
+// CHECK-NEXT: Str: linker comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:7:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:7:9"
+// CHECK-NEXT: Kind: user
+// CHECK-NEXT: Str: user comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:9:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDetectMismatch
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:9:9"
+// CHECK-NEXT: Name: name argument
+// CHECK-NEXT: Value: value argument
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:11:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:13:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaMessage
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:13:9"
+// CHECK-NEXT: Namespace:
+// CHECK-NEXT: Kind: PMK_Message
+// CHECK-NEXT: Str: message argument
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:15:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarningPush
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:15:9"
+// CHECK-NEXT: Level: 1
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:16:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarningPop
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:16:9"
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT: WarningSpec: disable
+// CHECK-NEXT: Ids: [1, 2, 3]
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT: WarningSpec: error
+// CHECK-NEXT: Ids: [4, 5, 6]
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT: WarningSpec: suppress
+// CHECK-NEXT: Ids: [7, 8, 9]
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -x cl | FileCheck --strict-whitespace %s
+
+#pragma OPENCL EXTENSION all : disable
+#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : disable
+#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT: NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:26"
+// CHECK-NEXT: Name: all
+// CHECK-NEXT: StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:32"
+// CHECK-NEXT: State: 0
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT: NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:26"
+// CHECK-NEXT: Name: cl_khr_int64_base_atomics
+// CHECK-NEXT: StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:54"
+// CHECK-NEXT: State: 0
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:1"
+// CHECK-NEXT: Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT: NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:26"
+// CHECK-NEXT: Name: cl_khr_int64_base_atomics
+// CHECK-NEXT: StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:54"
+// CHECK-NEXT: State: 1
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_executable(tool-template
+ ToolTemplate.cpp
+ )
+
+target_link_libraries(tool-template
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangTooling
+ )
--- /dev/null
+//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements an empty refactoring tool using the clang tooling.
+// The goal is to lower the "barrier to entry" for writing refactoring tools.
+//
+// Usage:
+// tool-template <cmake-output-dir> <file1> <file2> ...
+//
+// Where <cmake-output-dir> is a CMake build directory in which a file named
+// compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
+// CMake to get this output).
+//
+// <file1> ... specify the paths of files in the CMake source tree. This path
+// is looked up in the compile command database. If the path of a file is
+// absolute, it needs to point into CMake's source tree. If the path is
+// relative, the current working directory needs to be in the CMake source
+// tree and the file must be in a subdirectory of the current working
+// directory. "./" prefixes in the relative files will be automatically
+// removed, but the rest of a relative path must be a suffix of a path in
+// the compile command line database.
+//
+// For example, to use tool-template on all files in a subtree of the
+// source tree, use:
+//
+// /path/in/subtree $ find . -name '*.cpp'|
+// xargs tool-template /path/to/build
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace llvm;
+
+namespace {
+class ToolTemplateCallback : public MatchFinder::MatchCallback {
+public:
+ ToolTemplateCallback(std::map<std::string, Replacements> *Replace)
+ : Replace(Replace) {}
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ // TODO: This routine will get called for each thing that the matchers
+ // find.
+ // At this point, you can examine the match, and do whatever you want,
+ // including replacing the matched text with other text
+ (void)Replace; // This to prevent an "unused member variable" warning;
+ }
+
+private:
+ std::map<std::string, Replacements> *Replace;
+};
+} // end anonymous namespace
+
+// Set up the command line options
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ToolTemplateCategory("tool-template options");
+
+int main(int argc, const char **argv) {
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+ CommonOptionsParser OptionsParser(argc, argv, ToolTemplateCategory);
+ RefactoringTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+ ast_matchers::MatchFinder Finder;
+ ToolTemplateCallback Callback(&Tool.getReplacements());
+
+ // TODO: Put your matchers here.
+ // Use Finder.addMatcher(...) to define the patterns in the AST that you
+ // want to match against. You are not limited to just one matcher!
+
+ return Tool.run(newFrontendActionFactory(&Finder).get());
+}
--- /dev/null
+add_custom_target(ExtraToolsUnitTests)
+set_target_properties(ExtraToolsUnitTests PROPERTIES FOLDER "Extra Tools Unit Tests")
+
+function(add_extra_unittest test_dirname)
+ add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN})
+endfunction()
+
+add_subdirectory(change-namespace)
+add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-move)
+add_subdirectory(clang-query)
+add_subdirectory(clang-tidy)
+add_subdirectory(clangd)
+add_subdirectory(include-fixer)
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(CHANGE_NAMESPACE_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../change-namespace REALPATH)
+include_directories(
+ ${CHANGE_NAMESPACE_SOURCE_DIR}
+ )
+
+# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ChangeNamespaceTests
+ ChangeNamespaceTests.cpp
+ )
+
+target_link_libraries(ChangeNamespaceTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangChangeNamespace
+ clangFormat
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ )
--- /dev/null
+//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ChangeNamespace.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace change_namespace {
+namespace {
+
+class ChangeNamespaceTest : public ::testing::Test {
+public:
+ std::string runChangeNamespaceOnCode(llvm::StringRef Code) {
+ clang::RewriterTestContext Context;
+ clang::FileID ID = Context.createInMemoryFile(FileName, Code);
+
+ std::map<std::string, tooling::Replacements> FileToReplacements;
+ change_namespace::ChangeNamespaceTool NamespaceTool(
+ OldNamespace, NewNamespace, FilePattern,
+ /*WhiteListedSymbolPatterns*/ {}, &FileToReplacements);
+ ast_matchers::MatchFinder Finder;
+ NamespaceTool.registerMatchers(&Finder);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&Finder);
+ if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"},
+ FileName))
+ return "";
+ formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite);
+ return format(Context.getRewrittenText(ID));
+ }
+
+ std::string format(llvm::StringRef Code) {
+ tooling::Replacements Replaces = format::reformat(
+ format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())});
+ auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+ EXPECT_TRUE(static_cast<bool>(ChangedCode));
+ if (!ChangedCode) {
+ llvm::errs() << llvm::toString(ChangedCode.takeError());
+ return "";
+ }
+ return *ChangedCode;
+ }
+
+protected:
+ std::string FileName = "input.cc";
+ std::string OldNamespace = "na::nb";
+ std::string NewNamespace = "x::y";
+ std::string FilePattern = "input.cc";
+};
+
+TEST_F(ChangeNamespaceTest, NoMatchingNamespace) {
+ std::string Code = "namespace na {\n"
+ "namespace nx {\n"
+ "class A {};\n"
+ "} // namespace nx\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nx {\n"
+ "class A {};\n"
+ "} // namespace nx\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class A {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "\n\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class A {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNs) {
+ NewNamespace = "na::nb::nc";
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class A {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "namespace nc {\n"
+ "class A {};\n"
+ "} // namespace nc\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithSurroundingNewLines) {
+ NewNamespace = "na::nb::nc";
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "\n"
+ "class A {};\n"
+ "\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "namespace nc {\n"
+ "\n"
+ "class A {};\n"
+ "\n"
+ "} // namespace nc\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveOldNsWithSurroundingNewLines) {
+ NewNamespace = "nx::ny";
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "\n"
+ "class A {};\n"
+ "\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "\n\n"
+ "namespace nx {\n"
+ "namespace ny {\n"
+ "\n"
+ "class A {};\n"
+ "\n"
+ "} // namespace ny\n"
+ "} // namespace nx\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithRefs) {
+ NewNamespace = "na::nb::nc";
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "class C {};\n"
+ "void f() { A a; B b; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "namespace nc {\n"
+ "class B {};\n"
+ "class C {};\n"
+ "void f() { A a; B b; }\n"
+ "} // namespace nc\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) {
+ NewNamespace = "na::nc";
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class A {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "\n"
+ "namespace nc {\n"
+ "class A {};\n"
+ "} // namespace nc\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveIntoAnotherNestedNamespaceWithRef) {
+ NewNamespace = "na::nc";
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "class X { A a; };\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "\n"
+ "namespace nc {\n"
+ "class X { A a; };\n"
+ "} // namespace nc\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveIntoExistingNamespaceAndShortenRefs) {
+ std::string Code = "namespace x {\n"
+ "namespace z {\n"
+ "class Z {};\n"
+ "} // namespace z\n"
+ "namespace y {\n"
+ "class T {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n"
+ "namespace na {\n"
+ "class A{};\n"
+ "namespace nb {\n"
+ "class X { A a; x::z::Z zz; x::y::T t; };\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace x {\n"
+ "namespace z {\n"
+ "class Z {};\n"
+ "} // namespace z\n"
+ "namespace y {\n"
+ "class T {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n"
+ "namespace na {\n"
+ "class A {};\n\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class X { na::A a; z::Z zz; T t; };\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveNestedNamespace) {
+ NewNamespace = "na::x::y";
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class B {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithTypeRefs) {
+ std::string Code = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A a;\n"
+ " nc::C_C c;\n"
+ "};\n"
+ "class C_Y {\n"
+ " C_X x;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class C_X {\n"
+ "public:\n"
+ " na::C_A a;\n"
+ " na::nc::C_C c;\n"
+ "};\n"
+ "class C_Y {\n"
+ " C_X x;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeLocInTemplateSpecialization) {
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "template <typename T>\n"
+ "class B {};\n"
+ "template <typename T1, typename T2>\n"
+ "class Two {};\n"
+ "namespace nc { class C {}; }\n"
+ "} // na\n"
+ "\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " B<A> b;\n"
+ " B<nc::C> b_c;\n"
+ " Two<A, nc::C> two;\n"
+ "}\n"
+ "} // nb\n"
+ "} // na\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "template <typename T>\n"
+ "class B {};\n"
+ "template <typename T1, typename T2>\n"
+ "class Two {};\n"
+ "namespace nc { class C {}; }\n"
+ "} // na\n"
+ "\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " na::B<na::A> b;\n"
+ " na::B<na::nc::C> b_c;\n"
+ " na::Two<na::A, na::nc::C> two;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, LeaveForwardDeclarationBehind) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class FWD;\n"
+ "class FWD2;\n"
+ "class A {\n"
+ " FWD *fwd;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "class FWD;\n"
+ "class FWD2;\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "\n"
+ "class A {\n"
+ " na::nb::FWD *fwd;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, InsertForwardDeclsProperly) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "\n"
+ "class FWD;\n"
+ "class FWD2;\n"
+ "class A {\n"
+ " FWD *fwd;\n"
+ "};\n"
+ "\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "class FWD;\n"
+ "class FWD2;\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "\n"
+ "class A {\n"
+ " na::nb::FWD *fwd;\n"
+ "};\n"
+ "\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TemplateClassForwardDeclaration) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class FWD;\n"
+ "template<typename T> class FWD_TEMP;\n"
+ "class A {\n"
+ " FWD *fwd;\n"
+ "};\n"
+ "template<typename T> class TEMP {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "class FWD;\n"
+ "template<typename T> class FWD_TEMP;\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "\n"
+ "class A {\n"
+ " na::nb::FWD *fwd;\n"
+ "};\n"
+ "template<typename T> class TEMP {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DontMoveForwardDeclarationInClass) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class A {\n"
+ " class FWD;\n"
+ " FWD *fwd;\n"
+ " template<typename T> class FWD_TEMP;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "\n\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class A {\n"
+ " class FWD;\n"
+ " FWD *fwd;\n"
+ " template<typename T> class FWD_TEMP;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveFunctions) {
+ std::string Code = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "namespace nb {\n"
+ "void fwd();\n"
+ "void f(C_A ca, nc::C_C cc) {\n"
+ " C_A ca_1 = ca;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void fwd();\n"
+ "void f(na::C_A ca, na::nc::C_C cc) {\n"
+ " na::C_A ca_1 = ca;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixUsingShadowDecl) {
+ std::string Code = "class GLOB {};\n"
+ "using BLOG = GLOB;\n"
+ "namespace na {\n"
+ "namespace nc {\n"
+ "class SAME {};\n"
+ "}\n"
+ "namespace nd {\n"
+ "class SAME {};\n"
+ "}\n"
+ "namespace nb {\n"
+ "using nc::SAME;\n"
+ "using YO = nd::SAME;\n"
+ "typedef nd::SAME IDENTICAL;\n"
+ "void f(nd::SAME Same) {}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "class GLOB {};\n"
+ "using BLOG = GLOB;\n"
+ "namespace na {\n"
+ "namespace nc {\n"
+ "class SAME {};\n"
+ "}\n"
+ "namespace nd {\n"
+ "class SAME {};\n"
+ "}\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "using ::na::nc::SAME;\n"
+ "using YO = na::nd::SAME;\n"
+ "typedef na::nd::SAME IDENTICAL;\n"
+ "void f(na::nd::SAME Same) {}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DontFixUsingShadowDeclInClasses) {
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "class Base { public: Base() {} void m() {} };\n"
+ "namespace nb {\n"
+ "class D : public Base {\n"
+ "public:\n"
+ " using AA = A; using B = Base;\n"
+ " using Base::m; using Base::Base;\n"
+ "};"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "class Base { public: Base() {} void m() {} };\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class D : public na::Base {\n"
+ "public:\n"
+ " using AA = na::A; using B = na::Base;\n"
+ " using Base::m; using Base::Base;\n"
+ "};"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeInNestedNameSpecifier) {
+ std::string Code =
+ "namespace na {\n"
+ "class C_A {\n"
+ "public:\n"
+ " class Nested {\n"
+ " public:\n"
+ " static int NestedX;\n"
+ " static void nestedFunc() {}\n"
+ " };\n"
+ "};\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ " C_A na;\n"
+ " C_A::Nested nested;\n"
+ " void f() {\n"
+ " C_A::Nested::nestedFunc();\n"
+ " int X = C_A::Nested::NestedX;\n"
+ " }\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace na {\n"
+ "class C_A {\n"
+ "public:\n"
+ " class Nested {\n"
+ " public:\n"
+ " static int NestedX;\n"
+ " static void nestedFunc() {}\n"
+ " };\n"
+ "};\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class C_X {\n"
+ " na::C_A na;\n"
+ " na::C_A::Nested nested;\n"
+ " void f() {\n"
+ " na::C_A::Nested::nestedFunc();\n"
+ " int X = na::C_A::Nested::NestedX;\n"
+ " }\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixFunctionNameSpecifiers) {
+ std::string Code =
+ "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " static void f() {}\n"
+ " static void g();\n"
+ "};\n"
+ "void A::g() {}"
+ "void a_f() {}\n"
+ "static void static_f() {}\n"
+ "namespace nb {\n"
+ "void f() { a_f(); static_f(); A::f(); }\n"
+ "void g() { f(); A::g(); }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " static void f() {}\n"
+ " static void g();\n"
+ "};\n"
+ "void A::g() {}"
+ "void a_f() {}\n"
+ "static void static_f() {}\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { na::a_f(); na::static_f(); na::A::f(); }\n"
+ "void g() { f(); na::A::g(); }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixOverloadedOperatorFunctionNameSpecifiers) {
+ std::string Code =
+ "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " int x;\n"
+ " bool operator==(const A &RHS) const { return x == RHS.x; }\n"
+ "};\n"
+ "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n"
+ "namespace nb {\n"
+ "bool f() {\n"
+ " A x, y;\n"
+ " auto f = operator<;\n"
+ " return (x == y) && (x < y) && (operator<(x, y));\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " int x;\n"
+ " bool operator==(const A &RHS) const { return x == RHS.x; }\n"
+ "};\n"
+ "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "bool f() {\n"
+ " na::A x, y;\n"
+ " auto f = na::operator<;\n"
+ // FIXME: function calls to overloaded operators are not fixed now even if
+ // they are referenced by qualified names.
+ " return (x == y) && (x < y) && (operator<(x,y));\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixNonCallingFunctionReferences) {
+ std::string Code = "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " static void f() {}\n"
+ "};\n"
+ "void a_f() {}\n"
+ "static void s_f() {}\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " auto *ref1 = A::f;\n"
+ " auto *ref2 = a_f;\n"
+ " auto *ref3 = s_f;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ " static void f() {}\n"
+ "};\n"
+ "void a_f() {}\n"
+ "static void s_f() {}\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " auto *ref1 = na::A::f;\n"
+ " auto *ref2 = na::a_f;\n"
+ " auto *ref3 = na::s_f;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveAndFixGlobalVariables) {
+ std::string Code = "namespace na {\n"
+ "int GlobA;\n"
+ "static int GlobAStatic = 0;\n"
+ "namespace nc { int GlobC; }\n"
+ "namespace nb {\n"
+ "int GlobB;\n"
+ "void f() {\n"
+ " int a = GlobA;\n"
+ " int b = GlobAStatic;\n"
+ " int c = nc::GlobC;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace na {\n"
+ "int GlobA;\n"
+ "static int GlobAStatic = 0;\n"
+ "namespace nc { int GlobC; }\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "int GlobB;\n"
+ "void f() {\n"
+ " int a = na::GlobA;\n"
+ " int b = na::GlobAStatic;\n"
+ " int c = na::nc::GlobC;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DoNotFixStaticVariableOfClass) {
+ std::string Code = "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ "static int A1;\n"
+ "static int A2;\n"
+ "};\n"
+ "int A::A1 = 0;\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " int a = A::A1; int b = A::A2;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace na {\n"
+ "class A {\n"
+ "public:\n"
+ "static int A1;\n"
+ "static int A2;\n"
+ "};\n"
+ "int A::A1 = 0;\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " int a = na::A::A1; int b = na::A::A2;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NoMisplaceAtEOF) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "class A;\n"
+ "class B {};\n"
+ "}"
+ "}";
+ std::string Expected = "namespace na {\n"
+ "namespace nb {\n"
+ "class A;\n"
+ "}\n"
+ "}\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "\n"
+ "class B {};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, CommentsBeforeMovedClass) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "\n\n"
+ "// Wild comments.\n"
+ "\n"
+ "// Comments.\n"
+ "// More comments.\n"
+ "class B {\n"
+ " // Private comments.\n"
+ " int a;\n"
+ "};\n"
+ "}\n"
+ "}";
+ std::string Expected = "\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "\n\n"
+ "// Wild comments.\n"
+ "\n"
+ "// Comments.\n"
+ "// More comments.\n"
+ "class B {\n"
+ " // Private comments.\n"
+ " int a;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInGlobal) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using glob::Glob;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using glob::Glob;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using namespace glob;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using namespace glob;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace glob2 { class Glob2 {}; }\n"
+ "namespace gl = glob;\n"
+ "namespace gl2 = glob2;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { gl::Glob g; gl2::Glob2 g2; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected =
+ "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace glob2 { class Glob2 {}; }\n"
+ "namespace gl = glob;\n"
+ "namespace gl2 = glob2;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { gl::Glob g; gl2::Glob2 g2; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInNamespace) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "namespace gl = glob;\n"
+ "void f() { gl::Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "namespace gl = glob;\n"
+ "void f() { gl::Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInAncestorNamespace) {
+ NewNamespace = "na::nx";
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace other { namespace gl = glob; }\n"
+ "namespace na {\n"
+ "namespace ga = glob;\n"
+ "namespace nb {\n"
+ "void f() { ga::Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace other { namespace gl = glob; }\n"
+ "namespace na {\n"
+ "namespace ga = glob;\n"
+ "\n"
+ "namespace nx {\n"
+ "void f() { ga::Glob g; }\n"
+ "} // namespace nx\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInOtherNamespace) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace other { namespace gl = glob; }\n"
+ "namespace na {\n"
+ "namespace ga = glob;\n"
+ "namespace nb {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace other { namespace gl = glob; }\n"
+ "namespace na {\n"
+ "namespace ga = glob;\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "using glob::Glob;\n"
+ "using namespace glob;\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n"
+ "using glob::Glob;\n"
+ "using namespace glob;\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceAfterReference) {
+ NewNamespace = "na::nc";
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace nb\n"
+ "using namespace glob;\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace na {\n"
+ "\n"
+ "namespace nc {\n"
+ "void f() { glob::Glob g; }\n"
+ "} // namespace nc\n"
+ "using namespace glob;\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceAndUsingShadowInGlobal) {
+ std::string Code = "namespace glob1 {\n"
+ "namespace glob2 {\n"
+ "class Glob {};\n"
+ "}\n"
+ "}\n"
+ "using glob1::glob2::Glob;\n"
+ "using namespace glob1;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob1 {\n"
+ "namespace glob2 {\n"
+ "class Glob {};\n"
+ "}\n"
+ "}\n"
+ "using glob1::glob2::Glob;\n"
+ "using namespace glob1;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { Glob g; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasInGlobal) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using GLB = glob::Glob;\n"
+ "using BLG = glob::Glob;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { GLB g; BLG blg; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "using GLB = glob::Glob;\n"
+ "using BLG = glob::Glob;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { GLB g; BLG blg; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclAndMovedNamespace) {
+ std::string Code = "namespace na { class C_A {};\n }\n"
+ "using na::C_A;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A a;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na { class C_A {};\n }\n"
+ "using na::C_A;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A a;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceDeclAndMovedNamespace) {
+ std::string Code = "namespace na { class C_A {};\n }\n"
+ "using namespace na;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A ca;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na { class C_A {};\n }\n"
+ "using namespace na;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A ca;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInFunction) {
+ std::string Code = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " using glob::Glob;\n"
+ " Glob g;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace glob {\n"
+ "class Glob {};\n"
+ "}\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " using ::glob::Glob;\n"
+ " Glob g;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInClass) {
+ std::string Code = "namespace na { class C_A {}; }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " using ::na::C_A;\n"
+ " C_A ca;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na { class C_A {}; }\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " using ::na::C_A;\n"
+ " C_A ca;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespace) {
+ std::string Code = "namespace nx { void f(); }\n"
+ "namespace na {\n"
+ "using nx::f;\n"
+ "namespace nb {\n"
+ "void d() { f(); }\n"
+ "} // nb\n"
+ "} // na\n";
+
+ std::string Expected = "namespace nx { void f(); }\n"
+ "namespace na {\n"
+ "using nx::f;\n"
+ "\n"
+ "} // na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void d() { nx::f(); }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceNotNested) {
+ OldNamespace = "na";
+ std::string Code = "namespace nx { void f(); }\n"
+ "namespace na {\n"
+ "using ::nx::f;\n"
+ "void d() { f(); }\n"
+ "} // na\n";
+
+ std::string Expected = "namespace nx { void f(); }\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "using ::nx::f;\n"
+ "void d() { f(); }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceMultiNested) {
+ OldNamespace = "a::b::c::d";
+ NewNamespace = "a::b::x::y";
+ std::string Code = "namespace nx { void f(); void g(); }\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "using ::nx::f;\n"
+ "namespace c {\n"
+ "using ::nx::g;\n"
+ "namespace d {\n"
+ "void d() { f(); g(); }\n"
+ "} // d\n"
+ "} // c\n"
+ "} // b\n"
+ "} // a\n";
+
+ std::string Expected = "namespace nx { void f(); void g(); }\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "using ::nx::f;\n"
+ "namespace c {\n"
+ "using ::nx::g;\n"
+ "\n"
+ "} // c\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void d() { f(); nx::g(); }\n"
+ "} // namespace y\n"
+ "} // namespace x\n"
+ "} // b\n"
+ "} // a\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInTheParentOfOldNamespace) {
+ OldNamespace = "nb::nc";
+ NewNamespace = "nb::nd";
+ std::string Code = "namespace na { class A {}; }\n"
+ "namespace nb {\n"
+ "using na::A;\n"
+ "namespace nc {\n"
+ "void d() { A a; }\n"
+ "} // nc\n"
+ "} // nb\n";
+
+ std::string Expected = "namespace na { class A {}; }\n"
+ "namespace nb {\n"
+ "using na::A;\n"
+ "\n"
+ "namespace nd {\n"
+ "void d() { A a; }\n"
+ "} // namespace nd\n"
+ "} // nb\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInOldNamespace) {
+ OldNamespace = "nb";
+ NewNamespace = "nc";
+ std::string Code = "namespace na { class A {}; }\n"
+ "namespace nb {\n"
+ "using na::A;\n"
+ "void d() { A a; }\n"
+ "struct X { A a; };\n"
+ "} // nb\n";
+
+ std::string Expected = "namespace na { class A {}; }\n"
+ "\n"
+ "namespace nc {\n"
+ "using ::na::A;\n"
+ "void d() { A a; }\n"
+ "struct X { A a; };\n"
+ "} // namespace nc\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateClass) {
+ OldNamespace = "nb";
+ NewNamespace = "nc";
+ std::string Code = "namespace na {\n"
+ "template <typename T>\n"
+ "class A { T t; };\n"
+ "} // namespace na\n"
+ "namespace nb {\n"
+ "using na::A;\n"
+ "void d() { A<int> a; }\n"
+ "} // nb\n";
+
+ std::string Expected = "namespace na {\n"
+ "template <typename T>\n"
+ "class A { T t; };\n"
+ "} // namespace na\n"
+ "\n"
+ "namespace nc {\n"
+ "using ::na::A;\n"
+ "void d() { A<int> a; }\n"
+ "} // namespace nc\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateFunction) {
+ OldNamespace = "nb";
+ NewNamespace = "nc";
+ std::string Code = "namespace na {\n"
+ "template <typename T>\n"
+ "void f() { T t; };\n"
+ "} // namespace na\n"
+ "namespace nb {\n"
+ "using na::f;\n"
+ "void d() { f<int>(); }\n"
+ "} // nb\n";
+
+ std::string Expected = "namespace na {\n"
+ "template <typename T>\n"
+ "void f() { T t; };\n"
+ "} // namespace na\n"
+ "\n"
+ "namespace nc {\n"
+ "using ::na::f;\n"
+ "void d() { f<int>(); }\n"
+ "} // namespace nc\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasDecl) {
+ std::string Code =
+ "namespace nx { namespace ny { class X {}; } }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "using Y = nx::ny::X;\n"
+ "void f() { Y y; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "using Y = nx::ny::X;\n"
+ "void f() { Y y; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasDeclInGlobal) {
+ std::string Code =
+ "namespace nx { namespace ny { class X {}; } }\n"
+ "using Y = nx::ny::X;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() { Y y; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+ "using Y = nx::ny::X;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() { Y y; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+
+TEST_F(ChangeNamespaceTest, TypedefAliasDecl) {
+ std::string Code =
+ "namespace nx { namespace ny { class X {}; } }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "typedef nx::ny::X Y;\n"
+ "void f() { Y y; }\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+
+ std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "typedef nx::ny::X Y;\n"
+ "void f() { Y y; }\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithConstructors) {
+ std::string Code =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X(i) {}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "\n\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X(i) {}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithQualifiedConstructors) {
+ std::string Code =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X::X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X::X(i) {}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "\n\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X::X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X::X(i) {}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithConstructorsAndTypeRefs) {
+ std::string Code =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X(i) { X x(1);}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected =
+ "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+ "\n\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class A : public nx::ny::X {\n"
+ "public:\n"
+ " A() : X(0) {}\n"
+ " A(int i);\n"
+ "};\n"
+ "A::A(int i) : X(i) { nx::ny::X x(1);}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveToGlobalNamespace) {
+ NewNamespace = "";
+ std::string Code = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ "public:\n"
+ " C_A a;\n"
+ " nc::C_C c;\n"
+ "};\n"
+ "class C_Y {\n"
+ " C_X x;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "\n"
+ "} // namespace na\n"
+ "class C_X {\n"
+ "public:\n"
+ " na::C_A a;\n"
+ " na::nc::C_C c;\n"
+ "};\n"
+ "class C_Y {\n"
+ " C_X x;\n"
+ "};\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, KeepGlobalSpecifier) {
+ std::string Code = "class Glob {};\n"
+ "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "namespace nb {\n"
+ "class C_X {\n"
+ "public:\n"
+ " ::Glob glob_1;\n"
+ " Glob glob_2;\n"
+ " C_A a_1;\n"
+ " ::na::C_A a_2;\n"
+ " nc::C_C c;\n"
+ "};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "class Glob {};\n"
+ "namespace na {\n"
+ "class C_A {};\n"
+ "namespace nc {\n"
+ "class C_C {};"
+ "} // namespace nc\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "class C_X {\n"
+ "public:\n"
+ " ::Glob glob_1;\n"
+ " Glob glob_2;\n"
+ " na::C_A a_1;\n"
+ " ::na::C_A a_2;\n"
+ " na::nc::C_C c;\n"
+ "};\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasInTemplate) {
+ NewNamespace = "na::nb::nc";
+ std::string Code = "namespace some_ns {\n"
+ "template <typename T, typename S>\n"
+ "class G {};\n"
+ "} // namespace some_ns\n"
+ "namespace na {\n"
+ "template<typename P>\n"
+ "using GG = some_ns::G<int, P>;\n"
+ "} // namespace na\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " GG<float> g;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace some_ns {\n"
+ "template <typename T, typename S>\n"
+ "class G {};\n"
+ "} // namespace some_ns\n"
+ "namespace na {\n"
+ "template<typename P>\n"
+ "using GG = some_ns::G<int, P>;\n"
+ "} // namespace na\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "namespace nc {\n"
+ "void f() {\n"
+ " GG<float> g;\n"
+ "}\n"
+ "} // namespace nc\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TemplateUsingAliasInBaseClass) {
+ NewNamespace = "na::nb::nc";
+ std::string Code = "namespace some_ns {\n"
+ "template <typename T, typename S>\n"
+ "class G {};\n"
+ "} // namespace some_ns\n"
+ "namespace na {\n"
+ "class Base {\n"
+ "public:\n"
+ " template<typename P>\n"
+ " using GG = some_ns::G<int, P>;\n"
+ "\n"
+ " struct Nested {};\n"
+ "};\n"
+ "class Derived : public Base {};\n"
+ "} // namespace na\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " Derived::GG<float> g;\n"
+ " const Derived::GG<int> gg;\n"
+ " const Derived::GG<int>* gg_ptr;\n"
+ " struct Derived::Nested nested;\n"
+ " const struct Derived::Nested *nested_ptr;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace some_ns {\n"
+ "template <typename T, typename S>\n"
+ "class G {};\n"
+ "} // namespace some_ns\n"
+ "namespace na {\n"
+ "class Base {\n"
+ "public:\n"
+ " template<typename P>\n"
+ " using GG = some_ns::G<int, P>;\n"
+ "\n"
+ " struct Nested {};\n"
+ "};\n"
+ "class Derived : public Base {};\n"
+ "} // namespace na\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "namespace nc {\n"
+ "void f() {\n"
+ " Derived::GG<float> g;\n"
+ " const Derived::GG<int> gg;\n"
+ " const Derived::GG<int>* gg_ptr;\n"
+ " struct Derived::Nested nested;\n"
+ " const struct Derived::Nested *nested_ptr;\n"
+ "}\n"
+ "} // namespace nc\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ExistingNamespaceConflictWithNewNamespace) {
+ OldNamespace = "nx";
+ NewNamespace = "ny::na::nc";
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "} // namespace na\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "} // namespace nb\n"
+ "namespace nx {\n"
+ "class X {\n"
+ " na::A a; nb::B b;\n"
+ "};\n"
+ "} // namespace nx\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "} // namespace na\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "} // namespace nb\n"
+ "\n"
+ "namespace ny {\n"
+ "namespace na {\n"
+ "namespace nc {\n"
+ "class X {\n"
+ " ::na::A a; nb::B b;\n"
+ "};\n"
+ "} // namespace nc\n"
+ "} // namespace na\n"
+ "} // namespace ny\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SymbolConflictWithNewNamespace) {
+ OldNamespace = "nx";
+ NewNamespace = "ny::na::nc";
+ std::string Code = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "}\n"
+ "namespace nx {\n"
+ "class X {\n"
+ " na::A a; na::nb::B b;\n"
+ " ny::Y y;"
+ "};\n"
+ "} // namespace nx\n";
+ std::string Expected = "namespace na {\n"
+ "class A {};\n"
+ "namespace nb {\n"
+ "class B {};\n"
+ "} // namespace nb\n"
+ "} // namespace na\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "}\n"
+ "\n"
+ "namespace ny {\n"
+ "namespace na {\n"
+ "namespace nc {\n"
+ "class X {\n"
+ " ::na::A a; ::na::nb::B b;\n"
+ " Y y;\n"
+ "};\n"
+ "} // namespace nc\n"
+ "} // namespace na\n"
+ "} // namespace ny\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifier) {
+ OldNamespace = "nx";
+ NewNamespace = "ny::na";
+ std::string Code = "class G {};\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "namespace na {\n"
+ "class A {};\n"
+ "namespace nc { class C {}; } // namespace nc\n"
+ "}\n // namespace na\n"
+ "}\n // namespace ny\n"
+ "namespace nx {\n"
+ "class X {\n"
+ " G g; ny::Y y; ny::na::A a; ny::na::nc::C c;\n"
+ "};\n"
+ "} // namespace nx\n";
+ std::string Expected = "class G {};\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "namespace na {\n"
+ "class A {};\n"
+ "namespace nc { class C {}; } // namespace nc\n"
+ "}\n // namespace na\n"
+ "}\n // namespace ny\n"
+ "\n"
+ "namespace ny {\n"
+ "namespace na {\n"
+ "class X {\n"
+ " G g; Y y; A a; nc::C c;\n"
+ "};\n"
+ "} // namespace na\n"
+ "} // namespace ny\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifierInAnonymousNamespace) {
+ OldNamespace = "nx";
+ NewNamespace = "ny::na";
+ std::string Code = "class G {};\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "namespace na {\n"
+ "class A {};\n"
+ "namespace nc { class C {}; } // namespace nc\n"
+ "}\n // namespace na\n"
+ "}\n // namespace ny\n"
+ "namespace nx {\n"
+ "namespace {\n"
+ "class X {\n"
+ " G g; ::ny::Y y; ::ny::na::A a; ::ny::na::nc::C c;\n"
+ "};\n"
+ "} // namespace\n"
+ "} // namespace nx\n";
+ std::string Expected = "class G {};\n"
+ "namespace ny {\n"
+ "class Y {};\n"
+ "namespace na {\n"
+ "class A {};\n"
+ "namespace nc { class C {}; } // namespace nc\n"
+ "}\n // namespace na\n"
+ "}\n // namespace ny\n"
+ "\n"
+ "namespace ny {\n"
+ "namespace na {\n"
+ "namespace {\n"
+ "class X {\n"
+ " G g; Y y; A a; nc::C c;\n"
+ "};\n"
+ "} // namespace\n"
+ "} // namespace na\n"
+ "} // namespace ny\n";
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveEnum) {
+ std::string Code = "namespace na {\n"
+ "namespace nb {\n"
+ "enum class X { X1, X2 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "\n\nnamespace x {\n"
+ "namespace y {\n"
+ "enum class X { X1, X2 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ReferencesToEnums) {
+ std::string Code = "enum Glob { G1, G2 };\n"
+ "namespace na {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " Glob g1 = Glob::G1;\n"
+ " Glob g2 = G2;\n"
+ " X x1 = X::X1;\n"
+ " Y y1 = Y::Y1;\n"
+ " Y y2 = Y2;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "enum Glob { G1, G2 };\n"
+ "namespace na {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " Glob g1 = Glob::G1;\n"
+ " Glob g2 = G2;\n"
+ " na::X x1 = na::X::X1;\n"
+ " na::Y y1 = na::Y::Y1;\n"
+ " na::Y y2 = na::Y::Y2;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NoRedundantEnumUpdate) {
+ std::string Code = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "} // namespace ns\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " ns::X x1 = ns::X::X1;\n"
+ " ns::Y y1 = ns::Y::Y1;\n"
+ " ns::Y y2 = ns::Y2;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2 };\n"
+ "} // namespace ns\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " ns::X x1 = ns::X::X1;\n"
+ " ns::Y y1 = ns::Y::Y1;\n"
+ // FIXME: this is redundant
+ " ns::Y y2 = ns::Y::Y2;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+ ;
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumsAndUsingShadows) {
+ std::string Code = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2, Y3 };\n"
+ "} // namespace ns\n"
+ "using ns::X;\n"
+ "using ns::Y;\n"
+ "using ns::Y::Y2;\n"
+ "using ns::Y::Y3;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " X x1 = X::X1;\n"
+ " Y y1 = Y::Y1;\n"
+ " Y y2 = Y2;\n"
+ " Y y3 = Y3;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2, Y3 };\n"
+ "} // namespace ns\n"
+ "using ns::X;\n"
+ "using ns::Y;\n"
+ "using ns::Y::Y2;\n"
+ "using ns::Y::Y3;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " X x1 = X::X1;\n"
+ " Y y1 = Y::Y1;\n"
+ " Y y2 = Y2;\n"
+ " Y y3 = Y3;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumsAndAliases) {
+ std::string Code = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2, Y3 };\n"
+ "} // namespace ns\n"
+ "typedef ns::X TX;\n"
+ "typedef ns::Y TY;\n"
+ "using UX = ns::X;\n"
+ "using UY = ns::Y;\n"
+ "namespace na {\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " ns::X x1 = ns::X::X1;\n"
+ " TX tx1 = TX::X1;\n"
+ " UX ux1 = UX::X1;\n"
+ " ns::Y y1 = ns::Y::Y1;\n"
+ " TY ty1 = TY::Y1;\n"
+ " UY uy1 = UY::Y1;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace ns {\n"
+ "enum class X { X1 };\n"
+ "enum Y { Y1, Y2, Y3 };\n"
+ "} // namespace ns\n"
+ "typedef ns::X TX;\n"
+ "typedef ns::Y TY;\n"
+ "using UX = ns::X;\n"
+ "using UY = ns::Y;\n"
+ "\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " ns::X x1 = ns::X::X1;\n"
+ " TX tx1 = TX::X1;\n"
+ " UX ux1 = UX::X1;\n"
+ " ns::Y y1 = ns::Y::Y1;\n"
+ " TY ty1 = TY::Y1;\n"
+ " UY uy1 = UY::Y1;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumInClass) {
+ std::string Code = "namespace na {\n"
+ "struct X { enum E { E1 }; };\n"
+ "namespace nb {\n"
+ "void f() {\n"
+ " X::E e = X::E1;\n"
+ " X::E ee = X::E::E1;\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "struct X { enum E { E1 }; };\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "void f() {\n"
+ " na::X::E e = na::X::E1;\n"
+ " na::X::E ee = na::X::E::E1;\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeAsTemplateParameter) {
+ std::string Code = "namespace na {\n"
+ "struct X {};\n"
+ "namespace nb {\n"
+ "template <typename TT>\n"
+ "void TempTemp(const TT& t) {\n"
+ " TT tmp;\n"
+ "}\n"
+ "template <typename T>\n"
+ "void Temp(const T& t) {\n"
+ " T tmp = t;\n"
+ " TempTemp(tmp);\n"
+ " TempTemp(t);\n"
+ "}\n"
+ "void f() {\n"
+ " X x;\n"
+ " Temp(x);\n"
+ "}\n"
+ "} // namespace nb\n"
+ "} // namespace na\n";
+ std::string Expected = "namespace na {\n"
+ "struct X {};\n"
+ "\n"
+ "} // namespace na\n"
+ "namespace x {\n"
+ "namespace y {\n"
+ "template <typename TT>\n"
+ "void TempTemp(const TT& t) {\n"
+ " TT tmp;\n"
+ "}\n"
+ "template <typename T>\n"
+ "void Temp(const T& t) {\n"
+ " T tmp = t;\n"
+ " TempTemp(tmp);\n"
+ " TempTemp(t);\n"
+ "}\n"
+ "void f() {\n"
+ " na::X x;\n"
+ " Temp(x);\n"
+ "}\n"
+ "} // namespace y\n"
+ "} // namespace x\n";
+
+ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+} // anonymous namespace
+} // namespace change_namespace
+} // namespace clang
--- /dev/null
+//===- clang-apply-replacements/ApplyReplacementsTest.cpp
+//----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "gtest/gtest.h"
+
+using namespace clang::replace;
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+static TUDiagnostics
+makeTUDiagnostics(const std::string &MainSourceFile, StringRef DiagnosticName,
+ const DiagnosticMessage &Message,
+ const StringMap<Replacements> &Replacements,
+ StringRef BuildDirectory) {
+ TUDiagnostics TUs;
+ TUs.push_back({MainSourceFile,
+ {{DiagnosticName,
+ Message,
+ Replacements,
+ {},
+ Diagnostic::Warning,
+ BuildDirectory}}});
+ return TUs;
+}
+
+// Test to ensure diagnostics with no fixes, will be merged correctly
+// before applying.
+TEST(ApplyReplacementsTest, mergeDiagnosticsWithNoFixes) {
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ TUDiagnostics TUs =
+ makeTUDiagnostics("path/to/source.cpp", "diagnostic", {}, {}, "path/to");
+ FileToReplacementsMap ReplacementsMap;
+
+ EXPECT_TRUE(mergeAndDeduplicate(TUs, ReplacementsMap, SM));
+ EXPECT_TRUE(ReplacementsMap.empty());
+}
+
+} // end namespace tooling
+} // end namespace clang
--- /dev/null
+get_filename_component(ClangApplyReplacementsLocation
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../clang-apply-replacements/include" REALPATH)
+get_filename_component(CommonIncLocation
+ "${CMAKE_CURRENT_SOURCE_DIR}/../include" REALPATH)
+include_directories(
+ ${ClangApplyReplacementsLocation}
+ ${CommonIncLocation}
+ )
+
+add_extra_unittest(ClangApplyReplacementsTests
+ ApplyReplacementsTest.cpp
+ ReformattingTest.cpp
+ )
+
+target_link_libraries(ClangApplyReplacementsTests
+ clangApplyReplacements
+ clangBasic
+ clangToolingCore
+ )
--- /dev/null
+//===- clang-apply-replacements/ReformattingTest.cpp ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "common/VirtualFileHelper.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Refactoring.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::replace;
+
+typedef std::vector<clang::tooling::Replacement> ReplacementsVec;
+
+static Replacement makeReplacement(unsigned Offset, unsigned Length,
+ unsigned ReplacementLength,
+ llvm::StringRef FilePath = "") {
+ return Replacement(FilePath, Offset, Length,
+ std::string(ReplacementLength, '~'));
+}
+
+// generate a set of replacements containing one element
+static ReplacementsVec makeReplacements(unsigned Offset, unsigned Length,
+ unsigned ReplacementLength,
+ llvm::StringRef FilePath = "~") {
+ ReplacementsVec Replaces;
+ Replaces.push_back(
+ makeReplacement(Offset, Length, ReplacementLength, FilePath));
+ return Replaces;
+}
+
+// Put these functions in the clang::tooling namespace so arg-dependent name
+// lookup finds these functions for the EXPECT_EQ macros below.
+namespace clang {
+namespace tooling {
+
+std::ostream &operator<<(std::ostream &os, const Range &R) {
+ return os << "Range(" << R.getOffset() << ", " << R.getLength() << ")";
+}
+
+} // namespace tooling
+} // namespace clang
+
+// Ensure zero-length ranges are produced. Even lines where things are deleted
+// need reformatting.
+TEST(CalculateChangedRangesTest, producesZeroLengthRange) {
+ RangeVector Changes = calculateChangedRanges(makeReplacements(0, 4, 0));
+ EXPECT_EQ(Range(0, 0), Changes.front());
+}
+
+// Basic test to ensure replacements turn into ranges properly.
+TEST(CalculateChangedRangesTest, calculatesRanges) {
+ ReplacementsVec R;
+ R.push_back(makeReplacement(2, 0, 3));
+ R.push_back(makeReplacement(5, 2, 4));
+ RangeVector Changes = calculateChangedRanges(R);
+
+ Range ExpectedRanges[] = { Range(2, 3), Range(8, 4) };
+ EXPECT_TRUE(std::equal(Changes.begin(), Changes.end(), ExpectedRanges));
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-move REALPATH)
+include_directories(
+ ${INCLUDE_FIXER_SOURCE_DIR}
+ )
+
+# We'd like to clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ClangMoveTests
+ ClangMoveTests.cpp
+ )
+
+target_link_libraries(ClangMoveTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangMove
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ )
--- /dev/null
+//===-- ClangMoveTest.cpp - clang-move unit tests -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace move {
+namespace {
+
+const char TestHeaderName[] = "foo.h";
+
+const char TestCCName[] = "foo.cc";
+
+const char TestHeader[] = "namespace a {\n"
+ "class C1; // test\n"
+ "template <typename T> class C2;\n"
+ "namespace b {\n"
+ "// This is a Foo class\n"
+ "// which is used in\n"
+ "// test.\n"
+ "class Foo {\n"
+ "public:\n"
+ " void f();\n"
+ "\n"
+ "private:\n"
+ " C1 *c1;\n"
+ " static int b;\n"
+ "}; // abc\n"
+ "\n"
+ "class Foo2 {\n"
+ "public:\n"
+ " int f();\n"
+ "};\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+
+const char TestCC[] = "#include \"foo.h\"\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "namespace {\n"
+ "// comment1.\n"
+ "void f1() {}\n"
+ "/// comment2.\n"
+ "int kConstInt1 = 0;\n"
+ "} // namespace\n"
+ "\n"
+ "/* comment 3*/\n"
+ "static int kConstInt2 = 1;\n"
+ "\n"
+ "/** comment4\n"
+ " */\n"
+ "static int help() {\n"
+ " int a = 0;\n"
+ " return a;\n"
+ "}\n"
+ "\n"
+ "// comment5\n"
+ "// comment5\n"
+ "void Foo::f() {\n"
+ " f1();\n"
+ " kConstInt1;\n"
+ " kConstInt2;\n"
+ " help();\n"
+ "}\n"
+ "\n"
+ "/////////////\n"
+ "// comment //\n"
+ "/////////////\n"
+ "int Foo::b = 2;\n"
+ "int Foo2::f() {\n"
+ " kConstInt1;\n"
+ " kConstInt2;\n"
+ " help();\n"
+ " f1();\n"
+ " return 1;\n"
+ "}\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+
+const char ExpectedTestHeader[] = "namespace a {\n"
+ "class C1; // test\n"
+ "template <typename T> class C2;\n"
+ "namespace b {\n"
+ "\n"
+ "class Foo2 {\n"
+ "public:\n"
+ " int f();\n"
+ "};\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+
+const char ExpectedTestCC[] = "#include \"foo.h\"\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "namespace {\n"
+ "// comment1.\n"
+ "void f1() {}\n"
+ "/// comment2.\n"
+ "int kConstInt1 = 0;\n"
+ "} // namespace\n"
+ "\n"
+ "/* comment 3*/\n"
+ "static int kConstInt2 = 1;\n"
+ "\n"
+ "/** comment4\n"
+ " */\n"
+ "static int help() {\n"
+ " int a = 0;\n"
+ " return a;\n"
+ "}\n"
+ "\n"
+ "int Foo2::f() {\n"
+ " kConstInt1;\n"
+ " kConstInt2;\n"
+ " help();\n"
+ " f1();\n"
+ " return 1;\n"
+ "}\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+
+const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+ "#define NEW_FOO_H\n"
+ "\n"
+ "namespace a {\n"
+ "class C1; // test\n"
+ "\n"
+ "template <typename T> class C2;\n"
+ "namespace b {\n"
+ "// This is a Foo class\n"
+ "// which is used in\n"
+ "// test.\n"
+ "class Foo {\n"
+ "public:\n"
+ " void f();\n"
+ "\n"
+ "private:\n"
+ " C1 *c1;\n"
+ " static int b;\n"
+ "}; // abc\n"
+ "} // namespace b\n"
+ "} // namespace a\n"
+ "\n"
+ "#endif // NEW_FOO_H\n";
+
+const char ExpectedNewCC[] = "namespace a {\n"
+ "namespace b {\n"
+ "namespace {\n"
+ "// comment1.\n"
+ "void f1() {}\n"
+ "\n"
+ "/// comment2.\n"
+ "int kConstInt1 = 0;\n"
+ "} // namespace\n"
+ "\n"
+ "/* comment 3*/\n"
+ "static int kConstInt2 = 1;\n"
+ "\n"
+ "/** comment4\n"
+ " */\n"
+ "static int help() {\n"
+ " int a = 0;\n"
+ " return a;\n"
+ "}\n"
+ "\n"
+ "// comment5\n"
+ "// comment5\n"
+ "void Foo::f() {\n"
+ " f1();\n"
+ " kConstInt1;\n"
+ " kConstInt2;\n"
+ " help();\n"
+ "}\n"
+ "\n"
+ "/////////////\n"
+ "// comment //\n"
+ "/////////////\n"
+ "int Foo::b = 2;\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+
+std::map<std::string, std::string>
+runClangMoveOnCode(const move::MoveDefinitionSpec &Spec,
+ const char *const Header = TestHeader,
+ const char *const CC = TestCC,
+ DeclarationReporter *const Reporter = nullptr) {
+ clang::RewriterTestContext Context;
+
+ std::map<llvm::StringRef, clang::FileID> FileToFileID;
+ std::vector<std::pair<std::string, std::string>> FileToSourceText = {
+ {TestHeaderName, Header}, {TestCCName, CC}};
+
+ auto CreateFiles = [&Context, &FileToFileID](llvm::StringRef Name,
+ llvm::StringRef Code) {
+ if (!Name.empty()) {
+ FileToFileID[Name] = Context.createInMemoryFile(Name, Code);
+ }
+ };
+ CreateFiles(Spec.NewCC, "");
+ CreateFiles(Spec.NewHeader, "");
+ CreateFiles(Spec.OldHeader, Header);
+ CreateFiles(Spec.OldCC, CC);
+
+ std::map<std::string, tooling::Replacements> FileToReplacements;
+ llvm::SmallString<128> InitialDirectory;
+ std::error_code EC = llvm::sys::fs::current_path(InitialDirectory);
+ assert(!EC);
+ (void)EC;
+ ClangMoveContext MoveContext = {Spec, FileToReplacements,
+ InitialDirectory.str(), "LLVM",
+ Reporter != nullptr};
+
+ auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
+ &MoveContext, Reporter);
+
+ tooling::runToolOnCodeWithArgs(
+ Factory->create(), CC, {"-std=c++11", "-fparse-all-comments"},
+ TestCCName, "clang-move", std::make_shared<PCHContainerOperations>(),
+ FileToSourceText);
+ formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm");
+ // The Key is file name, value is the new code after moving the class.
+ std::map<std::string, std::string> Results;
+ for (const auto &It : FileToReplacements) {
+ StringRef FilePath = It.first;
+ Results[FilePath] = Context.getRewrittenText(FileToFileID[FilePath]);
+ }
+ return Results;
+}
+
+TEST(ClangMove, MoveHeaderAndCC) {
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {std::string("a::b::Foo")};
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ std::string ExpectedHeader = "#include \"" + Spec.NewHeader + "\"\n\n";
+ auto Results = runClangMoveOnCode(Spec);
+ EXPECT_EQ(ExpectedTestHeader, Results[Spec.OldHeader]);
+ EXPECT_EQ(ExpectedTestCC, Results[Spec.OldCC]);
+ EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+ EXPECT_EQ(ExpectedHeader + ExpectedNewCC, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, MoveHeaderOnly) {
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {std::string("a::b::Foo")};
+ Spec.OldHeader = "foo.h";
+ Spec.NewHeader = "new_foo.h";
+ auto Results = runClangMoveOnCode(Spec);
+ EXPECT_EQ(2u, Results.size());
+ EXPECT_EQ(ExpectedTestHeader, Results[Spec.OldHeader]);
+ EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, MoveCCOnly) {
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {std::string("a::b::Foo")};
+ Spec.OldCC = "foo.cc";
+ Spec.NewCC = "new_foo.cc";
+ std::string ExpectedHeader = "#include \"foo.h\"\n\n";
+ auto Results = runClangMoveOnCode(Spec);
+ EXPECT_EQ(2u, Results.size());
+ EXPECT_EQ(ExpectedTestCC, Results[Spec.OldCC]);
+ EXPECT_EQ(ExpectedHeader + ExpectedNewCC, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, MoveNonExistClass) {
+ move::MoveDefinitionSpec Spec;
+ Spec.Names = {std::string("NonExistFoo")};
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ auto Results = runClangMoveOnCode(Spec);
+ EXPECT_EQ(0u, Results.size());
+}
+
+TEST(ClangMove, MoveAll) {
+ std::vector<std::string> TestHeaders = {
+ "class A {\npublic:\n int f();\n};",
+ // forward declaration.
+ "class B;\nclass A {\npublic:\n int f();\n};",
+ // template forward declaration.
+ "template <typename T> class B;\nclass A {\npublic:\n int f();\n};",
+ "namespace a {}\nclass A {\npublic:\n int f();\n};",
+ "namespace a {}\nusing namespace a;\nclass A {\npublic:\n int f();\n};",
+ };
+ const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("A");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ for (const auto& Header : TestHeaders) {
+ auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+ EXPECT_EQ(Header, Results[Spec.NewHeader]);
+ EXPECT_EQ("", Results[Spec.OldHeader]);
+ EXPECT_EQ("", Results[Spec.OldCC]);
+ }
+}
+
+TEST(ClangMove, MoveAllMultipleClasses) {
+ move::MoveDefinitionSpec Spec;
+ std::vector<std::string> TestHeaders = {
+ "class C;\nclass A {\npublic:\n int f();\n};\nclass B {};",
+ "class C;\nclass B;\nclass A {\npublic:\n int f();\n};\nclass B {};",
+ };
+ const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+ Spec.Names = {std::string("A"), std::string("B")};
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ for (const auto& Header : TestHeaders) {
+ auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+ EXPECT_EQ(Header, Results[Spec.NewHeader]);
+ EXPECT_EQ("", Results[Spec.OldHeader]);
+ EXPECT_EQ("", Results[Spec.OldCC]);
+ }
+}
+
+TEST(ClangMove, DontMoveAll) {
+ const char ExpectedHeader[] = "#ifndef NEW_FOO_H\n"
+ "#define NEW_FOO_H\n"
+ "\n"
+ "class A {\npublic:\n int f();\n};\n"
+ "\n"
+ "#endif // NEW_FOO_H\n";
+ const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+ std::vector<std::string> TestHeaders = {
+ "class B {};\nclass A {\npublic:\n int f();\n};\n",
+ "void f() {};\nclass A {\npublic:\n int f();\n};\n",
+ };
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("A");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ for (const auto& Header : TestHeaders) {
+ auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+ EXPECT_EQ(ExpectedHeader, Results[Spec.NewHeader]);
+ // The expected old header should not contain class A definition.
+ std::string ExpectedOldHeader = Header.substr(0, Header.size() - 32);
+ EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+ }
+}
+
+TEST(ClangMove, MacroInFunction) {
+ const char TestHeader[] = "#define INT int\n"
+ "class A {\npublic:\n int f();\n};\n"
+ "class B {};\n";
+ const char TestCode[] = "#include \"foo.h\"\n"
+ "INT A::f() { return 0; }\n";
+ const char ExpectedNewCode[] = "#include \"new_foo.h\"\n\n"
+ "INT A::f() { return 0; }\n";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("A");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+ EXPECT_EQ(ExpectedNewCode, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, DefinitionInMacro) {
+ const char TestHeader[] = "#define DEF(CLASS) void CLASS##_::f() {}\n"
+ "class A_ {\nvoid f();\n};\n"
+ "class B {};\n";
+ const char TestCode[] = "#include \"foo.h\"\n"
+ "DEF(A)\n";
+ const char ExpectedNewCode[] = "#include \"new_foo.h\"\n\n"
+ "DEF(A)\n";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("A_");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+ EXPECT_EQ(ExpectedNewCode, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, WellFormattedCode) {
+ const std::string CommonHeader =
+ "namespace a {\n"
+ "namespace b {\n"
+ "namespace c {\n"
+ "class C;\n"
+ "\n"
+ "class A {\npublic:\n void f();\n void f2();\n};\n"
+ "} // namespace c\n"
+ "} // namespace b\n"
+ "\n"
+ "namespace d {\n"
+ "namespace e {\n"
+ "class B {\npublic:\n void f();\n};\n"
+ "} // namespace e\n"
+ "} // namespace d\n"
+ "} // namespace a\n";
+ const std::string CommonCode = "\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "namespace c {\n"
+ "void A::f() {}\n"
+ "\n"
+ "void A::f2() {}\n"
+ "} // namespace c\n"
+ "} // namespace b\n"
+ "\n"
+ "namespace d {\n"
+ "namespace e {\n"
+ "void B::f() {}\n"
+ "} // namespace e\n"
+ "} // namespace d\n"
+ "} // namespace a\n";
+ // Add dummy class to prevent behavior of moving all declarations from header.
+ const std::string TestHeader = CommonHeader + "class D {};\n";
+ const std::string TestCode = "#include \"foo.h\"\n" + CommonCode;
+ const std::string ExpectedNewHeader = "#ifndef NEW_FOO_H\n"
+ "#define NEW_FOO_H\n"
+ "\n" +
+ CommonHeader +
+ "\n"
+ "#endif // NEW_FOO_H\n";
+ const std::string ExpectedNewCC = "#include \"new_foo.h\"\n" + CommonCode;
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("a::b::c::A");
+ Spec.Names.push_back("a::d::e::B");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ auto Results = runClangMoveOnCode(Spec, TestHeader.c_str(), TestCode.c_str());
+ EXPECT_EQ(ExpectedNewCC, Results[Spec.NewCC]);
+ EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, AddDependentNewHeader) {
+ const char TestHeader[] = "class A {};\n"
+ "class B {};\n";
+ const char TestCode[] = "#include \"foo.h\"\n";
+ const char ExpectedOldHeader[] = "#include \"new_foo.h\"\nclass B {};\n";
+ const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+ "#define NEW_FOO_H\n"
+ "\n"
+ "class A {};\n"
+ "\n"
+ "#endif // NEW_FOO_H\n";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("A");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ Spec.OldDependOnNew = true;
+ auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+ EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+ EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, AddDependentOldHeader) {
+ const char TestHeader[] = "class A {};\n"
+ "class B {};\n";
+ const char TestCode[] = "#include \"foo.h\"\n";
+ const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+ "#define NEW_FOO_H\n"
+ "\n"
+ "#include \"foo.h\"\n"
+ "\n"
+ "class B {};\n"
+ "\n"
+ "#endif // NEW_FOO_H\n";
+ const char ExpectedOldHeader[] = "class A {};\n";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("B");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ Spec.NewDependOnOld = true;
+ auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+ EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+ EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+}
+
+TEST(ClangMove, DumpDecls) {
+ const char TestHeader[] = "template <typename T>\n"
+ "class A {\n"
+ " public:\n"
+ " void f();\n"
+ " template <typename U> void h();\n"
+ " static int b;\n"
+ "};\n"
+ "\n"
+ "template <typename T> void A<T>::f() {}\n"
+ "\n"
+ "template <typename T>\n"
+ "template <typename U>\n"
+ "void A<T>::h() {}\n"
+ "\n"
+ "template <typename T> int A<T>::b = 2;\n"
+ "\n"
+ "template <> class A<int> {};\n"
+ "\n"
+ "class B {};\n"
+ "\n"
+ "namespace a {\n"
+ "class Move1 {};\n"
+ "void f1() {}\n"
+ "void f2();\n"
+ "} // namespace a\n"
+ "\n"
+ "class ForwardClass;\n"
+ "namespace a {\n"
+ "namespace b {\n"
+ "class Move1 { public : void f(); };\n"
+ "void f() {}\n"
+ "enum E1 { Green };\n"
+ "enum class E2 { Red };\n"
+ "typedef int Int2;\n"
+ "typedef A<double> A_d;"
+ "using Int = int;\n"
+ "extern int kGlobalInt;\n"
+ "extern const char* const kGlobalStr;\n"
+ "} // namespace b\n"
+ "} // namespace a\n";
+ const char TestCode[] = "#include \"foo.h\"\n";
+ move::MoveDefinitionSpec Spec;
+ Spec.Names.push_back("B");
+ Spec.OldHeader = "foo.h";
+ Spec.OldCC = "foo.cc";
+ Spec.NewHeader = "new_foo.h";
+ Spec.NewCC = "new_foo.cc";
+ DeclarationReporter Reporter;
+ std::set<DeclarationReporter::DeclarationPair> ExpectedDeclarations = {
+ {"A", "Class"},
+ {"B", "Class"},
+ {"a::Move1", "Class"},
+ {"a::f1", "Function"},
+ {"a::f2", "Function"},
+ {"a::b::Move1", "Class"},
+ {"a::b::f", "Function"},
+ {"a::b::E1", "Enum"},
+ {"a::b::E2", "Enum"},
+ {"a::b::Int2", "TypeAlias"},
+ {"a::b::A_d", "TypeAlias"},
+ {"a::b::Int", "TypeAlias"},
+ {"a::b::kGlobalInt", "Variable"},
+ {"a::b::kGlobalStr", "Variable"}};
+ runClangMoveOnCode(Spec, TestHeader, TestCode, &Reporter);
+ std::set<DeclarationReporter::DeclarationPair> Results;
+ for (const auto& DelPair : Reporter.getDeclarationList())
+ Results.insert(DelPair);
+ EXPECT_EQ(ExpectedDeclarations, Results);
+}
+
+} // namespace
+} // namespce move
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-query
+ )
+
+add_extra_unittest(ClangQueryTests
+ QueryEngineTest.cpp
+ QueryParserTest.cpp
+ )
+
+target_link_libraries(ClangQueryTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangDynamicASTMatchers
+ clangFrontend
+ clangQuery
+ clangTooling
+ )
--- /dev/null
+//===---- QueryTest.cpp - clang-query test --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QueryParser.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+using namespace clang::query;
+using namespace clang::tooling;
+
+class QueryEngineTest : public ::testing::Test {
+ ArrayRef<std::unique_ptr<ASTUnit>> mkASTUnit2(std::unique_ptr<ASTUnit> a,
+ std::unique_ptr<ASTUnit> b) {
+ ASTs[0] = std::move(a);
+ ASTs[1] = std::move(b);
+ return ArrayRef<std::unique_ptr<ASTUnit>>(ASTs);
+ }
+
+protected:
+ QueryEngineTest()
+ : S(mkASTUnit2(buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}",
+ "foo.cc"),
+ buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}",
+ "bar.cc"))),
+ OS(Str) {}
+
+ std::unique_ptr<ASTUnit> ASTs[2];
+ QuerySession S;
+
+ std::string Str;
+ llvm::raw_string_ostream OS;
+};
+
+TEST_F(QueryEngineTest, Basic) {
+ DynTypedMatcher FnMatcher = functionDecl();
+ DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
+
+ EXPECT_TRUE(NoOpQuery().run(OS, S));
+
+ EXPECT_EQ("", OS.str());
+
+ Str.clear();
+
+ EXPECT_FALSE(InvalidQuery("Parse error").run(OS, S));
+
+ EXPECT_EQ("Parse error\n", OS.str());
+
+ Str.clear();
+
+ EXPECT_TRUE(HelpQuery().run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("Available commands:") != std::string::npos);
+
+ Str.clear();
+
+ EXPECT_TRUE(MatchQuery(FnMatcher).run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("foo.cc:2:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("bar.cc:1:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("bar.cc:2:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("4 matches.") != std::string::npos);
+
+ Str.clear();
+
+ EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+
+ Str.clear();
+
+ EXPECT_TRUE(
+ SetQuery<OutputKind>(&QuerySession::OutKind, OK_Print).run(OS, S));
+ EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") !=
+ std::string::npos);
+
+ Str.clear();
+
+ EXPECT_TRUE(SetQuery<OutputKind>(&QuerySession::OutKind, OK_Dump).run(OS, S));
+ EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos);
+
+ Str.clear();
+
+ EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S));
+ EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+ EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos);
+
+ Str.clear();
+
+ EXPECT_FALSE(MatchQuery(isArrow()).run(OS, S));
+
+ EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
+}
+
+TEST_F(QueryEngineTest, LetAndMatch) {
+ EXPECT_TRUE(QueryParser::parse("let x \"foo1\"", S)->run(OS, S));
+ EXPECT_EQ("", OS.str());
+ Str.clear();
+
+ EXPECT_TRUE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+ EXPECT_EQ("", OS.str());
+ Str.clear();
+
+ EXPECT_TRUE(QueryParser::parse("match functionDecl(y)", S)->run(OS, S));
+ EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+ std::string::npos);
+ EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+ Str.clear();
+
+ EXPECT_TRUE(QueryParser::parse("unlet x", S)->run(OS, S));
+ EXPECT_EQ("", OS.str());
+ Str.clear();
+
+ EXPECT_FALSE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+ EXPECT_EQ("1:2: Error parsing argument 1 for matcher hasName.\n"
+ "1:10: Value not found: x\n", OS.str());
+ Str.clear();
+}
--- /dev/null
+//===---- QueryParserTest.cpp - clang-query test --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "QuerySession.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::query;
+
+class QueryParserTest : public ::testing::Test {
+protected:
+ QueryParserTest() : QS(llvm::ArrayRef<std::unique_ptr<ASTUnit>>()) {}
+ QueryRef parse(StringRef Code) { return QueryParser::parse(Code, QS); }
+
+ QuerySession QS;
+};
+
+TEST_F(QueryParserTest, NoOp) {
+ QueryRef Q = parse("");
+ EXPECT_TRUE(isa<NoOpQuery>(Q));
+
+ Q = parse("\n");
+ EXPECT_TRUE(isa<NoOpQuery>(Q));
+}
+
+TEST_F(QueryParserTest, Invalid) {
+ QueryRef Q = parse("foo");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Help) {
+ QueryRef Q = parse("help");
+ ASSERT_TRUE(isa<HelpQuery>(Q));
+
+ Q = parse("help me");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Set) {
+ QueryRef Q = parse("set");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set foo bar");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set output");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected 'diag', 'print' or 'dump', got ''",
+ cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set bind-root true foo");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set output foo");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'",
+ cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set output dump");
+ ASSERT_TRUE(isa<SetQuery<OutputKind> >(Q));
+ EXPECT_EQ(&QuerySession::OutKind, cast<SetQuery<OutputKind> >(Q)->Var);
+ EXPECT_EQ(OK_Dump, cast<SetQuery<OutputKind> >(Q)->Value);
+
+ Q = parse("set bind-root foo");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected 'true' or 'false', got 'foo'",
+ cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("set bind-root true");
+ ASSERT_TRUE(isa<SetQuery<bool> >(Q));
+ EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
+ EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
+}
+
+TEST_F(QueryParserTest, Match) {
+ QueryRef Q = parse("match decl()");
+ ASSERT_TRUE(isa<MatchQuery>(Q));
+ EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
+
+ Q = parse("m stmt()");
+ ASSERT_TRUE(isa<MatchQuery>(Q));
+ EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
+}
+
+TEST_F(QueryParserTest, LetUnlet) {
+ QueryRef Q = parse("let foo decl()");
+ ASSERT_TRUE(isa<LetQuery>(Q));
+ EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
+ EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
+ EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
+
+ Q = parse("let bar \"str\"");
+ ASSERT_TRUE(isa<LetQuery>(Q));
+ EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);
+ EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());
+ EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());
+
+ Q = parse("let");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("unlet x");
+ ASSERT_TRUE(isa<LetQuery>(Q));
+ EXPECT_EQ("x", cast<LetQuery>(Q)->Name);
+ EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());
+
+ Q = parse("unlet");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+ Q = parse("unlet x bad_data");
+ ASSERT_TRUE(isa<InvalidQuery>(Q));
+ EXPECT_EQ("unexpected extra input: ' bad_data'",
+ cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Complete) {
+ std::vector<llvm::LineEditor::Completion> Comps =
+ QueryParser::complete("", 0, QS);
+ ASSERT_EQ(6u, Comps.size());
+ EXPECT_EQ("help ", Comps[0].TypedText);
+ EXPECT_EQ("help", Comps[0].DisplayText);
+ EXPECT_EQ("let ", Comps[1].TypedText);
+ EXPECT_EQ("let", Comps[1].DisplayText);
+ EXPECT_EQ("match ", Comps[2].TypedText);
+ EXPECT_EQ("match", Comps[2].DisplayText);
+ EXPECT_EQ("set ", Comps[3].TypedText);
+ EXPECT_EQ("set", Comps[3].DisplayText);
+ EXPECT_EQ("unlet ", Comps[4].TypedText);
+ EXPECT_EQ("unlet", Comps[4].DisplayText);
+ EXPECT_EQ("quit", Comps[5].DisplayText);
+ EXPECT_EQ("quit ", Comps[5].TypedText);
+
+ Comps = QueryParser::complete("set o", 5, QS);
+ ASSERT_EQ(1u, Comps.size());
+ EXPECT_EQ("utput ", Comps[0].TypedText);
+ EXPECT_EQ("output", Comps[0].DisplayText);
+
+ Comps = QueryParser::complete("match while", 11, QS);
+ ASSERT_EQ(1u, Comps.size());
+ EXPECT_EQ("Stmt(", Comps[0].TypedText);
+ EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
+ Comps[0].DisplayText);
+}
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(CLANG_LINT_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy REALPATH)
+include_directories(${CLANG_LINT_SOURCE_DIR})
+
+add_extra_unittest(ClangTidyTests
+ ClangTidyDiagnosticConsumerTest.cpp
+ ClangTidyOptionsTest.cpp
+ IncludeInserterTest.cpp
+ GoogleModuleTest.cpp
+ LLVMModuleTest.cpp
+ MiscModuleTest.cpp
+ NamespaceAliaserTest.cpp
+ OverlappingReplacementsTest.cpp
+ UsingInserterTest.cpp
+ ReadabilityModuleTest.cpp)
+
+target_link_libraries(ClangTidyTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTidy
+ clangTidyAndroidModule
+ clangTidyGoogleModule
+ clangTidyLLVMModule
+ clangTidyMiscModule
+ clangTidyReadabilityModule
+ clangTidyUtils
+ clangTooling
+ clangToolingCore
+ )
--- /dev/null
+#include "ClangTidy.h"\r
+#include "ClangTidyTest.h"\r
+#include "gtest/gtest.h"\r
+\r
+namespace clang {\r
+namespace tidy {\r
+namespace test {\r
+\r
+class TestCheck : public ClangTidyCheck {\r
+public:\r
+ TestCheck(StringRef Name, ClangTidyContext *Context)\r
+ : ClangTidyCheck(Name, Context) {}\r
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override {\r
+ Finder->addMatcher(ast_matchers::varDecl().bind("var"), this);\r
+ }\r
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {\r
+ const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var");\r
+ // Add diagnostics in the wrong order.\r
+ diag(Var->getLocation(), "variable");\r
+ diag(Var->getTypeSpecStartLoc(), "type specifier");\r
+ }\r
+};\r
+\r
+TEST(ClangTidyDiagnosticConsumer, SortsErrors) {\r
+ std::vector<ClangTidyError> Errors;\r
+ runCheckOnCode<TestCheck>("int a;", &Errors);\r
+ EXPECT_EQ(2ul, Errors.size());\r
+ EXPECT_EQ("type specifier", Errors[0].Message.Message);\r
+ EXPECT_EQ("variable", Errors[1].Message.Message);\r
+}\r
+\r
+TEST(GlobList, Empty) {\r
+ GlobList Filter("");\r
+\r
+ EXPECT_TRUE(Filter.contains(""));\r
+ EXPECT_FALSE(Filter.contains("aaa"));\r
+}\r
+\r
+TEST(GlobList, Nothing) {\r
+ GlobList Filter("-*");\r
+\r
+ EXPECT_FALSE(Filter.contains(""));\r
+ EXPECT_FALSE(Filter.contains("a"));\r
+ EXPECT_FALSE(Filter.contains("-*"));\r
+ EXPECT_FALSE(Filter.contains("-"));\r
+ EXPECT_FALSE(Filter.contains("*"));\r
+}\r
+\r
+TEST(GlobList, Everything) {\r
+ GlobList Filter("*");\r
+\r
+ EXPECT_TRUE(Filter.contains(""));\r
+ EXPECT_TRUE(Filter.contains("aaaa"));\r
+ EXPECT_TRUE(Filter.contains("-*"));\r
+ EXPECT_TRUE(Filter.contains("-"));\r
+ EXPECT_TRUE(Filter.contains("*"));\r
+}\r
+\r
+TEST(GlobList, Simple) {\r
+ GlobList Filter("aaa");\r
+\r
+ EXPECT_TRUE(Filter.contains("aaa"));\r
+ EXPECT_FALSE(Filter.contains(""));\r
+ EXPECT_FALSE(Filter.contains("aa"));\r
+ EXPECT_FALSE(Filter.contains("aaaa"));\r
+ EXPECT_FALSE(Filter.contains("bbb"));\r
+}\r
+\r
+TEST(GlobList, WhitespacesAtBegin) {\r
+ GlobList Filter("-*, a.b.*");\r
+\r
+ EXPECT_TRUE(Filter.contains("a.b.c"));\r
+ EXPECT_FALSE(Filter.contains("b.c"));\r
+}\r
+\r
+TEST(GlobList, Complex) {\r
+ GlobList Filter("*,-a.*, -b.*, a.1.* ,-a.1.A.*,-..,-...,-..+,-*$, -*qwe* ");\r
+\r
+ EXPECT_TRUE(Filter.contains("aaa"));\r
+ EXPECT_TRUE(Filter.contains("qqq"));\r
+ EXPECT_FALSE(Filter.contains("a."));\r
+ EXPECT_FALSE(Filter.contains("a.b"));\r
+ EXPECT_FALSE(Filter.contains("b."));\r
+ EXPECT_FALSE(Filter.contains("b.b"));\r
+ EXPECT_TRUE(Filter.contains("a.1.b"));\r
+ EXPECT_FALSE(Filter.contains("a.1.A.a"));\r
+ EXPECT_FALSE(Filter.contains("qwe"));\r
+ EXPECT_FALSE(Filter.contains("asdfqweasdf"));\r
+ EXPECT_TRUE(Filter.contains("asdfqwEasdf"));\r
+}\r
+\r
+} // namespace test\r
+} // namespace tidy\r
+} // namespace clang\r
--- /dev/null
+#include "ClangTidyOptions.h"
+#include "gtest/gtest.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+TEST(ParseLineFilter, EmptyFilter) {
+ ClangTidyGlobalOptions Options;
+ EXPECT_FALSE(parseLineFilter("", Options));
+ EXPECT_TRUE(Options.LineFilter.empty());
+ EXPECT_FALSE(parseLineFilter("[]", Options));
+ EXPECT_TRUE(Options.LineFilter.empty());
+}
+
+TEST(ParseLineFilter, InvalidFilter) {
+ ClangTidyGlobalOptions Options;
+ EXPECT_TRUE(!!parseLineFilter("asdf", Options));
+ EXPECT_TRUE(Options.LineFilter.empty());
+
+ EXPECT_TRUE(!!parseLineFilter("[{}]", Options));
+ EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options));
+ EXPECT_TRUE(
+ !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options));
+ EXPECT_TRUE(
+ !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options));
+ EXPECT_TRUE(
+ !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options));
+}
+
+TEST(ParseLineFilter, ValidFilter) {
+ ClangTidyGlobalOptions Options;
+ std::error_code Error = parseLineFilter(
+ "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"
+ "{\"name\":\"file2.h\"},"
+ "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]",
+ Options);
+ EXPECT_FALSE(Error);
+ EXPECT_EQ(3u, Options.LineFilter.size());
+ EXPECT_EQ("file1.cpp", Options.LineFilter[0].Name);
+ EXPECT_EQ(3u, Options.LineFilter[0].LineRanges.size());
+ EXPECT_EQ(3u, Options.LineFilter[0].LineRanges[0].first);
+ EXPECT_EQ(15u, Options.LineFilter[0].LineRanges[0].second);
+ EXPECT_EQ(20u, Options.LineFilter[0].LineRanges[1].first);
+ EXPECT_EQ(30u, Options.LineFilter[0].LineRanges[1].second);
+ EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].first);
+ EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].second);
+ EXPECT_EQ("file2.h", Options.LineFilter[1].Name);
+ EXPECT_EQ(0u, Options.LineFilter[1].LineRanges.size());
+ EXPECT_EQ("file3.cc", Options.LineFilter[2].Name);
+ EXPECT_EQ(1u, Options.LineFilter[2].LineRanges.size());
+ EXPECT_EQ(100u, Options.LineFilter[2].LineRanges[0].first);
+ EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second);
+}
+
+TEST(ParseConfiguration, ValidConfiguration) {
+ llvm::ErrorOr<ClangTidyOptions> Options =
+ parseConfiguration("Checks: \"-*,misc-*\"\n"
+ "HeaderFilterRegex: \".*\"\n"
+ "AnalyzeTemporaryDtors: true\n"
+ "User: some.user");
+ EXPECT_TRUE(!!Options);
+ EXPECT_EQ("-*,misc-*", *Options->Checks);
+ EXPECT_EQ(".*", *Options->HeaderFilterRegex);
+ EXPECT_TRUE(*Options->AnalyzeTemporaryDtors);
+ EXPECT_EQ("some.user", *Options->User);
+}
+
+TEST(ParseConfiguration, MergeConfigurations) {
+ llvm::ErrorOr<ClangTidyOptions> Options1 = parseConfiguration(R"(
+ Checks: "check1,check2"
+ HeaderFilterRegex: "filter1"
+ AnalyzeTemporaryDtors: true
+ User: user1
+ ExtraArgs: ['arg1', 'arg2']
+ ExtraArgsBefore: ['arg-before1', 'arg-before2']
+ )");
+ ASSERT_TRUE(!!Options1);
+ llvm::ErrorOr<ClangTidyOptions> Options2 = parseConfiguration(R"(
+ Checks: "check3,check4"
+ HeaderFilterRegex: "filter2"
+ AnalyzeTemporaryDtors: false
+ User: user2
+ ExtraArgs: ['arg3', 'arg4']
+ ExtraArgsBefore: ['arg-before3', 'arg-before4']
+ )");
+ ASSERT_TRUE(!!Options2);
+ ClangTidyOptions Options = Options1->mergeWith(*Options2);
+ EXPECT_EQ("check1,check2,check3,check4", *Options.Checks);
+ EXPECT_EQ("filter2", *Options.HeaderFilterRegex);
+ EXPECT_FALSE(*Options.AnalyzeTemporaryDtors);
+ EXPECT_EQ("user2", *Options.User);
+ ASSERT_TRUE(Options.ExtraArgs.hasValue());
+ EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(),
+ Options.ExtraArgs->end(), ","));
+ ASSERT_TRUE(Options.ExtraArgsBefore.hasValue());
+ EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",
+ llvm::join(Options.ExtraArgsBefore->begin(),
+ Options.ExtraArgsBefore->end(), ","));
+}
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===--- ClangTidyTest.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_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
+
+#include "ClangTidy.h"
+#include "ClangTidyDiagnosticConsumer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include <map>
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+class TestClangTidyAction : public ASTFrontendAction {
+public:
+ TestClangTidyAction(SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks,
+ ast_matchers::MatchFinder &Finder,
+ ClangTidyContext &Context)
+ : Checks(Checks), Finder(Finder), Context(Context) {}
+
+private:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef File) override {
+ Context.setSourceManager(&Compiler.getSourceManager());
+ Context.setCurrentFile(File);
+ Context.setASTContext(&Compiler.getASTContext());
+
+ for (auto &Check : Checks) {
+ Check->registerMatchers(&Finder);
+ Check->registerPPCallbacks(Compiler);
+ }
+ return Finder.newASTConsumer();
+ }
+
+ SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks;
+ ast_matchers::MatchFinder &Finder;
+ ClangTidyContext &Context;
+};
+
+template <typename Check, typename... Checks> struct CheckFactory {
+ static void
+ createChecks(ClangTidyContext *Context,
+ SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
+ CheckFactory<Check>::createChecks(Context, Result);
+ CheckFactory<Checks...>::createChecks(Context, Result);
+ }
+};
+
+template <typename Check> struct CheckFactory<Check> {
+ static void
+ createChecks(ClangTidyContext *Context,
+ SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
+ Result.emplace_back(llvm::make_unique<Check>(
+ "test-check-" + std::to_string(Result.size()), Context));
+ }
+};
+
+template <typename... CheckList>
+std::string
+runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
+ const Twine &Filename = "input.cc",
+ ArrayRef<std::string> ExtraArgs = None,
+ const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),
+ std::map<StringRef, StringRef> PathsToContent =
+ std::map<StringRef, StringRef>()) {
+ ClangTidyOptions Options = ExtraOptions;
+ Options.Checks = "*";
+ ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
+ ClangTidyGlobalOptions(), Options));
+ ClangTidyDiagnosticConsumer DiagConsumer(Context);
+
+ std::vector<std::string> ArgCXX11(1, "clang-tidy");
+ ArgCXX11.push_back("-fsyntax-only");
+ ArgCXX11.push_back("-std=c++11");
+ ArgCXX11.push_back("-Iinclude");
+ ArgCXX11.insert(ArgCXX11.end(), ExtraArgs.begin(), ExtraArgs.end());
+ ArgCXX11.push_back(Filename.str());
+
+ ast_matchers::MatchFinder Finder;
+ llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+ new vfs::InMemoryFileSystem);
+ llvm::IntrusiveRefCntPtr<FileManager> Files(
+ new FileManager(FileSystemOptions(), InMemoryFileSystem));
+
+ SmallVector<std::unique_ptr<ClangTidyCheck>, 1> Checks;
+ CheckFactory<CheckList...>::createChecks(&Context, Checks);
+ tooling::ToolInvocation Invocation(
+ ArgCXX11, new TestClangTidyAction(Checks, Finder, Context), Files.get());
+ InMemoryFileSystem->addFile(Filename, 0,
+ llvm::MemoryBuffer::getMemBuffer(Code));
+ for (const auto &FileContent : PathsToContent) {
+ InMemoryFileSystem->addFile(
+ Twine("include/") + FileContent.first, 0,
+ llvm::MemoryBuffer::getMemBuffer(FileContent.second));
+ }
+ Invocation.setDiagnosticConsumer(&DiagConsumer);
+ if (!Invocation.run()) {
+ std::string ErrorText;
+ for (const auto &Error : Context.getErrors()) {
+ ErrorText += Error.Message.Message + "\n";
+ }
+ llvm::report_fatal_error(ErrorText);
+ }
+
+ DiagConsumer.finish();
+ tooling::Replacements Fixes;
+ for (const ClangTidyError &Error : Context.getErrors()) {
+ for (const auto &FileAndFixes : Error.Fix) {
+ for (const auto &Fix : FileAndFixes.second) {
+ auto Err = Fixes.add(Fix);
+ // FIXME: better error handling. Keep the behavior for now.
+ if (Err) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ return "";
+ }
+ }
+ }
+ }
+ if (Errors)
+ *Errors = Context.getErrors();
+ auto Result = tooling::applyAllReplacements(Code, Fixes);
+ if (!Result) {
+ // FIXME: propogate the error.
+ llvm::consumeError(Result.takeError());
+ return "";
+ }
+ return *Result;
+}
+
+#define EXPECT_NO_CHANGES(Check, Code) \
+ EXPECT_EQ(Code, runCheckOnCode<Check>(Code))
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
--- /dev/null
+#include "ClangTidyTest.h"
+#include "google/ExplicitConstructorCheck.h"
+#include "google/GlobalNamesInHeadersCheck.h"
+#include "gtest/gtest.h"
+
+using namespace clang::tidy::google;
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+TEST(ExplicitConstructorCheckTest, SingleArgumentConstructorsOnly) {
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(); };");
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(int i, int j); };");
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(const C&); };");
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(C&&); };");
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck,
+ "class C { C(const C&) = delete; };");
+ EXPECT_NO_CHANGES(ExplicitConstructorCheck,
+ "class C { C(int) = delete; };");
+}
+
+TEST(ExplicitConstructorCheckTest, Basic) {
+ EXPECT_EQ("class C { explicit C(int i); };",
+ runCheckOnCode<ExplicitConstructorCheck>("class C { C(int i); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, DefaultParameters) {
+ EXPECT_EQ("class C { explicit C(int i, int j = 0); };",
+ runCheckOnCode<ExplicitConstructorCheck>(
+ "class C { C(int i, int j = 0); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, OutOfLineDefinitions) {
+ EXPECT_EQ("class C { explicit C(int i); }; C::C(int i) {}",
+ runCheckOnCode<ExplicitConstructorCheck>(
+ "class C { C(int i); }; C::C(int i) {}"));
+}
+
+TEST(ExplicitConstructorCheckTest, RemoveExplicit) {
+ EXPECT_EQ("class A { A(const A&); };\n"
+ "class B { /*asdf*/ B(B&&); };\n"
+ "class C { /*asdf*/ C(const C&, int i = 0); };",
+ runCheckOnCode<ExplicitConstructorCheck>(
+ "class A { explicit A(const A&); };\n"
+ "class B { explicit /*asdf*/ B(B&&); };\n"
+ "class C { explicit/*asdf*/ C(const C&, int i = 0); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, RemoveExplicitWithMacros) {
+ EXPECT_EQ(
+ "#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
+ "A(Foo);",
+ runCheckOnCode<ExplicitConstructorCheck>(
+ "#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
+ "A(Foo);"));
+}
+
+class GlobalNamesInHeadersCheckTest : public ::testing::Test {
+protected:
+ bool runCheckOnCode(const std::string &Code, const std::string &Filename) {
+ static const char *const Header = "namespace std {\n"
+ "class string {};\n"
+ "} // namespace std\n"
+ "\n"
+ "#define SOME_MACRO(x) using x\n";
+ std::vector<ClangTidyError> Errors;
+ std::vector<std::string> Args;
+ if (!StringRef(Filename).endswith(".cpp")) {
+ Args.emplace_back("-xc++-header");
+ }
+ test::runCheckOnCode<readability::GlobalNamesInHeadersCheck>(
+ Header + Code, &Errors, Filename, Args);
+ if (Errors.empty())
+ return false;
+ assert(Errors.size() == 1);
+ assert(
+ Errors[0].Message.Message ==
+ "using declarations in the global namespace in headers are prohibited");
+ return true;
+ }
+};
+
+TEST_F(GlobalNamesInHeadersCheckTest, UsingDeclarations) {
+ EXPECT_TRUE(runCheckOnCode("using std::string;", "foo.h"));
+ EXPECT_FALSE(runCheckOnCode("using std::string;", "foo.cpp"));
+ EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
+ "using std::string;\n"
+ "} // my_namespace\n",
+ "foo.h"));
+ EXPECT_FALSE(runCheckOnCode("SOME_MACRO(std::string);", "foo.h"));
+}
+
+TEST_F(GlobalNamesInHeadersCheckTest, UsingDirectives) {
+ EXPECT_TRUE(runCheckOnCode("using namespace std;", "foo.h"));
+ EXPECT_FALSE(runCheckOnCode("using namespace std;", "foo.cpp"));
+ EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
+ "using namespace std;\n"
+ "} // my_namespace\n",
+ "foo.h"));
+ EXPECT_FALSE(runCheckOnCode("SOME_MACRO(namespace std);", "foo.h"));
+}
+
+TEST_F(GlobalNamesInHeadersCheckTest, RegressionAnonymousNamespace) {
+ EXPECT_FALSE(runCheckOnCode("namespace {}", "foo.h"));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---- IncludeInserterTest.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 "../clang-tidy/utils/IncludeInserter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "ClangTidyTest.h"
+#include "gtest/gtest.h"
+
+// FIXME: Canonicalize paths correctly on windows.
+// Currently, adding virtual files will canonicalize the paths before
+// storing the virtual entries.
+// When resolving virtual entries in the FileManager, the paths (for
+// example coming from a #include directive) are not canonicalized
+// to native paths; thus, the virtual file is not found.
+// This needs to be fixed in the FileManager before we can make
+// clang-tidy tests work.
+#if !defined(_WIN32)
+
+namespace clang {
+namespace tidy {
+namespace {
+
+class IncludeInserterCheckBase : public ClangTidyCheck {
+public:
+ IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
+ : ClangTidyCheck(CheckName, Context) {}
+
+ void registerPPCallbacks(CompilerInstance &Compiler) override {
+ Inserter.reset(new utils::IncludeInserter(
+ Compiler.getSourceManager(),
+ Compiler.getLangOpts(),
+ utils::IncludeSorter::IS_Google));
+ Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+ }
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+ Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this);
+ }
+
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ auto Diag = diag(Result.Nodes.getNodeAs<DeclStmt>("stmt")->getLocStart(),
+ "foo, bar");
+ for (StringRef header : HeadersToInclude()) {
+ auto Fixit = Inserter->CreateIncludeInsertion(
+ Result.SourceManager->getMainFileID(), header, IsAngledInclude());
+ if (Fixit) {
+ Diag << *Fixit;
+ }
+ }
+ }
+
+ virtual std::vector<StringRef> HeadersToInclude() const = 0;
+ virtual bool IsAngledInclude() const = 0;
+
+ std::unique_ptr<utils::IncludeInserter> Inserter;
+};
+
+class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+ NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+ : IncludeInserterCheckBase(CheckName, Context) {}
+
+ std::vector<StringRef> HeadersToInclude() const override {
+ return {"path/to/header.h"};
+ }
+ bool IsAngledInclude() const override { return false; }
+};
+
+class EarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+ EarlyInAlphabetHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+ : IncludeInserterCheckBase(CheckName, Context) {}
+
+ std::vector<StringRef> HeadersToInclude() const override {
+ return {"a/header.h"};
+ }
+ bool IsAngledInclude() const override { return false; }
+};
+
+class MultipleHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+ MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+ : IncludeInserterCheckBase(CheckName, Context) {}
+
+ std::vector<StringRef> HeadersToInclude() const override {
+ return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};
+ }
+ bool IsAngledInclude() const override { return false; }
+};
+
+class CSystemIncludeInserterCheck : public IncludeInserterCheckBase {
+public:
+ CSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+ : IncludeInserterCheckBase(CheckName, Context) {}
+
+ std::vector<StringRef> HeadersToInclude() const override {
+ return {"stdlib.h"};
+ }
+ bool IsAngledInclude() const override { return true; }
+};
+
+class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {
+public:
+ CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+ : IncludeInserterCheckBase(CheckName, Context) {}
+
+ std::vector<StringRef> HeadersToInclude() const override { return {"set"}; }
+ bool IsAngledInclude() const override { return true; }
+};
+
+template <typename Check>
+std::string runCheckOnCode(StringRef Code, StringRef Filename) {
+ std::vector<ClangTidyError> Errors;
+ return test::runCheckOnCode<Check>(Code, &Errors, Filename, None,
+ ClangTidyOptions(),
+ {// Main file include
+ {"clang_tidy/tests/"
+ "insert_includes_test_header.h",
+ "\n"},
+ // Non system headers
+ {"a/header.h", "\n"},
+ {"path/to/a/header.h", "\n"},
+ {"path/to/z/header.h", "\n"},
+ {"path/to/header.h", "\n"},
+ {"path/to/header2.h", "\n"},
+ // Fake system headers.
+ {"stdlib.h", "\n"},
+ {"unistd.h", "\n"},
+ {"list", "\n"},
+ {"map", "\n"},
+ {"set", "\n"},
+ {"vector", "\n"}});
+}
+
+TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/header2.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<MultipleHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/z/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ EXPECT_EQ(PreCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <stdlib.h>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <stdlib.h>
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) {
+ const char *PreCode = R"(
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "devtools/cymbal/clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) {
+ const char *PreCode = R"(
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(
+#include <stdlib.h>
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CSystemIncludeInserterCheck>(
+ PreCode, "devtools/cymbal/clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) {
+ const char *PreCode = R"(
+void foo() {
+ int a = 0;
+})";
+ const char *PostCode = R"(#include <set>
+
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+ PreCode, "devtools/cymbal/clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(
+ PreCode, "workspace_folder/clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, HandleOrderInSubdirectory) {
+ const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+ int a = 0;
+})";
+
+ EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(
+ PreCode, "workspace_folder/clang_tidy/tests/"
+ "insert_includes_test_header.cc"));
+}
+
+} // anonymous namespace
+} // namespace tidy
+} // namespace clang
+
+#endif
--- /dev/null
+#include "ClangTidyTest.h"
+#include "llvm/HeaderGuardCheck.h"
+#include "llvm/IncludeOrderCheck.h"
+#include "gtest/gtest.h"
+
+using namespace clang::tidy::llvm;
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+// FIXME: It seems this might be incompatible to dos path. Investigating.
+#if !defined(_WIN32)
+static std::string runHeaderGuardCheck(StringRef Code, const Twine &Filename,
+ Optional<StringRef> ExpectedWarning) {
+ std::vector<ClangTidyError> Errors;
+ std::string Result = test::runCheckOnCode<LLVMHeaderGuardCheck>(
+ Code, &Errors, Filename, std::string("-xc++-header"));
+ if (Errors.size() != (size_t)ExpectedWarning.hasValue())
+ return "invalid error count";
+ if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
+ return "expected: '" + ExpectedWarning->str() + "', saw: '" +
+ Errors.back().Message.Message + "'";
+ return Result;
+}
+
+namespace {
+struct WithEndifComment : public LLVMHeaderGuardCheck {
+ WithEndifComment(StringRef Name, ClangTidyContext *Context)
+ : LLVMHeaderGuardCheck(Name, Context) {}
+ bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
+};
+} // namespace
+
+static std::string
+runHeaderGuardCheckWithEndif(StringRef Code, const Twine &Filename,
+ Optional<StringRef> ExpectedWarning) {
+ std::vector<ClangTidyError> Errors;
+ std::string Result = test::runCheckOnCode<WithEndifComment>(
+ Code, &Errors, Filename, std::string("-xc++-header"));
+ if (Errors.size() != (size_t)ExpectedWarning.hasValue())
+ return "invalid error count";
+ if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
+ return "expected: '" + ExpectedWarning->str() + "', saw: '" +
+ Errors.back().Message.Message + "'";
+ return Result;
+}
+
+TEST(LLVMHeaderGuardCheckTest, FixHeaderGuards) {
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif\n",
+ runHeaderGuardCheck(
+ "#ifndef FOO\n"
+ "#define FOO\n"
+ "#endif\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("header guard does not follow preferred style")));
+
+ // Allow trailing underscores.
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
+ "#define LLVM_ADT_FOO_H_\n"
+ "#endif\n",
+ runHeaderGuardCheck("#ifndef LLVM_ADT_FOO_H_\n"
+ "#define LLVM_ADT_FOO_H_\n"
+ "#endif\n",
+ "include/llvm/ADT/foo.h", None));
+
+ EXPECT_EQ("#ifndef LLVM_CLANG_C_BAR_H\n"
+ "#define LLVM_CLANG_C_BAR_H\n"
+ "\n"
+ "\n"
+ "#endif\n",
+ runHeaderGuardCheck("", "./include/clang-c/bar.h",
+ StringRef("header is missing header guard")));
+
+ EXPECT_EQ("#ifndef LLVM_CLANG_LIB_CODEGEN_C_H\n"
+ "#define LLVM_CLANG_LIB_CODEGEN_C_H\n"
+ "\n"
+ "\n"
+ "#endif\n",
+ runHeaderGuardCheck("", "tools/clang/lib/CodeGen/c.h",
+ StringRef("header is missing header guard")));
+
+ EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
+ "#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
+ "\n"
+ "\n"
+ "#endif\n",
+ runHeaderGuardCheck("", "tools/clang/tools/extra/clang-tidy/x.h",
+ StringRef("header is missing header guard")));
+
+ EXPECT_EQ(
+ "int foo;\n"
+ "#ifndef LLVM_CLANG_BAR_H\n"
+ "#define LLVM_CLANG_BAR_H\n"
+ "#endif\n",
+ runHeaderGuardCheck("int foo;\n"
+ "#ifndef LLVM_CLANG_BAR_H\n"
+ "#define LLVM_CLANG_BAR_H\n"
+ "#endif\n",
+ "include/clang/bar.h",
+ StringRef("code/includes outside of area guarded by "
+ "header guard; consider moving it")));
+
+ EXPECT_EQ(
+ "#ifndef LLVM_CLANG_BAR_H\n"
+ "#define LLVM_CLANG_BAR_H\n"
+ "#endif\n"
+ "int foo;\n",
+ runHeaderGuardCheck("#ifndef LLVM_CLANG_BAR_H\n"
+ "#define LLVM_CLANG_BAR_H\n"
+ "#endif\n"
+ "int foo;\n",
+ "include/clang/bar.h",
+ StringRef("code/includes outside of area guarded by "
+ "header guard; consider moving it")));
+
+ EXPECT_EQ("#ifndef LLVM_CLANG_BAR_H\n"
+ "#define LLVM_CLANG_BAR_H\n"
+ "\n"
+ "int foo;\n"
+ "#ifndef FOOLOLO\n"
+ "#define FOOLOLO\n"
+ "#endif\n"
+ "\n"
+ "#endif\n",
+ runHeaderGuardCheck("int foo;\n"
+ "#ifndef FOOLOLO\n"
+ "#define FOOLOLO\n"
+ "#endif\n",
+ "include/clang/bar.h",
+ StringRef("header is missing header guard")));
+
+ // Fix incorrect #endif comments even if we shouldn't add new ones.
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif // LLVM_ADT_FOO_H\n",
+ runHeaderGuardCheck(
+ "#ifndef FOO\n"
+ "#define FOO\n"
+ "#endif // FOO\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("header guard does not follow preferred style")));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif // LLVM_ADT_FOO_H\n",
+ runHeaderGuardCheckWithEndif(
+ "#ifndef FOO\n"
+ "#define FOO\n"
+ "#endif\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("header guard does not follow preferred style")));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif // LLVM_ADT_FOO_H\n",
+ runHeaderGuardCheckWithEndif(
+ "#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif // LLVM_H\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("#endif for a header guard should reference the "
+ "guard macro in a comment")));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif /* LLVM_ADT_FOO_H */\n",
+ runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif /* LLVM_ADT_FOO_H */\n",
+ "include/llvm/ADT/foo.h", None));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
+ "#define LLVM_ADT_FOO_H_\n"
+ "#endif // LLVM_ADT_FOO_H_\n",
+ runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H_\n"
+ "#define LLVM_ADT_FOO_H_\n"
+ "#endif // LLVM_ADT_FOO_H_\n",
+ "include/llvm/ADT/foo.h", None));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif // LLVM_ADT_FOO_H\n",
+ runHeaderGuardCheckWithEndif(
+ "#ifndef LLVM_ADT_FOO_H_\n"
+ "#define LLVM_ADT_FOO_H_\n"
+ "#endif // LLVM\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("header guard does not follow preferred style")));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif \\ \n"
+ "// LLVM_ADT_FOO_H\n",
+ runHeaderGuardCheckWithEndif(
+ "#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif \\ \n"
+ "// LLVM_ADT_FOO_H\n",
+ "include/llvm/ADT/foo.h",
+ StringRef("backslash and newline separated by space")));
+
+ EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif /* LLVM_ADT_FOO_H\\ \n"
+ " FOO */",
+ runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
+ "#define LLVM_ADT_FOO_H\n"
+ "#endif /* LLVM_ADT_FOO_H\\ \n"
+ " FOO */",
+ "include/llvm/ADT/foo.h", None));
+}
+#endif
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+#include "ClangTidyTest.h"
+#include "misc/ArgumentCommentCheck.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using misc::ArgumentCommentCheck;
+
+TEST(ArgumentCommentCheckTest, CorrectComments) {
+ EXPECT_NO_CHANGES(ArgumentCommentCheck,
+ "void f(int x, int y); void g() { f(/*x=*/0, /*y=*/0); }");
+ EXPECT_NO_CHANGES(ArgumentCommentCheck,
+ "struct C { C(int x, int y); }; C c(/*x=*/0, /*y=*/0);");
+}
+
+TEST(ArgumentCommentCheckTest, ThisEditDistanceAboveThreshold) {
+ EXPECT_NO_CHANGES(ArgumentCommentCheck,
+ "void f(int xxx); void g() { f(/*xyz=*/0); }");
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceAboveThreshold) {
+ EXPECT_EQ("void f(int xxx, int yyy); void g() { f(/*xxx=*/0, 0); }",
+ runCheckOnCode<ArgumentCommentCheck>(
+ "void f(int xxx, int yyy); void g() { f(/*Zxx=*/0, 0); }"));
+ EXPECT_EQ("struct C { C(int xxx, int yyy); }; C c(/*xxx=*/0, 0);",
+ runCheckOnCode<ArgumentCommentCheck>(
+ "struct C { C(int xxx, int yyy); }; C c(/*Zxx=*/0, 0);"));
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceBelowThreshold) {
+ EXPECT_NO_CHANGES(ArgumentCommentCheck,
+ "void f(int xxx, int yyy); void g() { f(/*xxy=*/0, 0); }");
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---- NamespaceAliaserTest.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 "../clang-tidy/utils/NamespaceAliaser.h"
+
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+// This checker is for testing only. It can only run on one test case
+// (e.g. with one SourceManager).
+class InsertAliasCheck : public ClangTidyCheck {
+public:
+ InsertAliasCheck(StringRef Name, ClangTidyContext *Context)
+ :ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+ Finder->addMatcher(ast_matchers::callExpr().bind("foo"), this);
+ }
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ if (!Aliaser)
+ Aliaser.reset(new NamespaceAliaser(*Result.SourceManager));
+
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("foo");
+ assert(Call != nullptr && "Did not find node \"foo\"");
+ auto Hint = Aliaser->createAlias(*Result.Context, *Call, "::foo::bar",
+ {"b", "some_alias"});
+ if (Hint.hasValue())
+ diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
+
+ diag(Call->getLocStart(), "insert call")
+ << FixItHint::CreateInsertion(
+ Call->getLocStart(),
+ Aliaser->getNamespaceName(*Result.Context, *Call, "::foo::bar") +
+ "::");
+ }
+
+private:
+ std::unique_ptr<NamespaceAliaser> Aliaser;
+};
+
+template <typename Check>
+std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
+ std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
+ "namespace foo {\n"
+ "namespace bar {\n"
+ "}\n"
+ "void func() { }\n"
+ "}"}};
+ std::vector<ClangTidyError> errors;
+
+ std::string result =
+ test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
+ ClangTidyOptions(), AdditionalFileContents);
+
+ EXPECT_EQ(ExpectedWarningCount, errors.size());
+ return result;
+}
+
+TEST(NamespaceAliaserTest, AddNewAlias) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "void f() {\n"
+ "namespace b = ::foo::bar;"
+ " b::f(); }",
+ runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+ "void f() { f(); }",
+ 2));
+}
+
+TEST(NamespaceAliaserTest, ReuseAlias) {
+ EXPECT_EQ(
+ "#include \"foo.h\"\n"
+ "void f() { namespace x = foo::bar; x::f(); }",
+ runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+ "void f() { namespace x = foo::bar; f(); }",
+ 1));
+}
+
+TEST(NamespaceAliaserTest, AddsOnlyOneAlias) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "void f() {\n"
+ "namespace b = ::foo::bar;"
+ " b::f(); b::f(); }",
+ runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+ "void f() { f(); f(); }",
+ 3));
+}
+
+TEST(NamespaceAliaserTest, LocalConflict) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "void f() {\n"
+ "namespace some_alias = ::foo::bar;"
+ " namespace b = foo; some_alias::f(); }",
+ runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+ "void f() { namespace b = foo; f(); }",
+ 2));
+}
+
+TEST(NamespaceAliaserTest, GlobalConflict) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "namespace b = foo;\n"
+ "void f() {\n"
+ "namespace some_alias = ::foo::bar;"
+ " some_alias::f(); }",
+ runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+ "namespace b = foo;\n"
+ "void f() { f(); }",
+ 2));
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---- OverlappingReplacementsTest.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 "ClangTidyTest.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+namespace {
+
+const char BoundDecl[] = "decl";
+const char BoundIf[] = "if";
+
+// We define a reduced set of very small checks that allow to test different
+// overlapping situations (no overlapping, replacements partially overlap, etc),
+// as well as different kinds of diagnostics (one check produces several errors,
+// several replacement ranges in an error, etc).
+class UseCharCheck : public ClangTidyCheck {
+public:
+ UseCharCheck(StringRef CheckName, ClangTidyContext *Context)
+ : ClangTidyCheck(CheckName, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+ using namespace ast_matchers;
+ Finder->addMatcher(varDecl(hasType(isInteger())).bind(BoundDecl), this);
+ }
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
+ diag(VD->getLocStart(), "use char") << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(VD->getLocStart(), VD->getLocStart()),
+ "char");
+ }
+};
+
+class IfFalseCheck : public ClangTidyCheck {
+public:
+ IfFalseCheck(StringRef CheckName, ClangTidyContext *Context)
+ : ClangTidyCheck(CheckName, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+ using namespace ast_matchers;
+ Finder->addMatcher(ifStmt().bind(BoundIf), this);
+ }
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+ auto *If = Result.Nodes.getNodeAs<IfStmt>(BoundIf);
+ auto *Cond = If->getCond();
+ SourceRange Range = Cond->getSourceRange();
+ if (auto *D = If->getConditionVariable()) {
+ Range = SourceRange(D->getLocStart(), D->getLocEnd());
+ }
+ diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Range), "false");
+ }
+};
+
+class RefactorCheck : public ClangTidyCheck {
+public:
+ RefactorCheck(StringRef CheckName, ClangTidyContext *Context)
+ : ClangTidyCheck(CheckName, Context), NamePattern("::$") {}
+ RefactorCheck(StringRef CheckName, ClangTidyContext *Context,
+ StringRef NamePattern)
+ : ClangTidyCheck(CheckName, Context), NamePattern(NamePattern) {}
+ virtual std::string newName(StringRef OldName) = 0;
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) final {
+ using namespace ast_matchers;
+ Finder->addMatcher(varDecl(matchesName(NamePattern)).bind(BoundDecl), this);
+ }
+
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) final {
+ auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
+ std::string NewName = newName(VD->getName());
+
+ auto Diag = diag(VD->getLocation(), "refactor %0 into %1")
+ << VD->getName() << NewName
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(VD->getLocation(),
+ VD->getLocation()),
+ NewName);
+
+ class UsageVisitor : public RecursiveASTVisitor<UsageVisitor> {
+ public:
+ UsageVisitor(const ValueDecl *VD, StringRef NewName,
+ DiagnosticBuilder &Diag)
+ : VD(VD), NewName(NewName), Diag(Diag) {}
+ bool VisitDeclRefExpr(DeclRefExpr *E) {
+ if (const ValueDecl *D = E->getDecl()) {
+ if (VD->getCanonicalDecl() == D->getCanonicalDecl()) {
+ Diag << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(E->getSourceRange()), NewName);
+ }
+ }
+ return RecursiveASTVisitor<UsageVisitor>::VisitDeclRefExpr(E);
+ }
+
+ private:
+ const ValueDecl *VD;
+ StringRef NewName;
+ DiagnosticBuilder &Diag;
+ };
+
+ UsageVisitor(VD, NewName, Diag)
+ .TraverseDecl(Result.Context->getTranslationUnitDecl());
+ }
+
+protected:
+ const std::string NamePattern;
+};
+
+class StartsWithPotaCheck : public RefactorCheck {
+public:
+ StartsWithPotaCheck(StringRef CheckName, ClangTidyContext *Context)
+ : RefactorCheck(CheckName, Context, "::pota") {}
+
+ std::string newName(StringRef OldName) override {
+ return "toma" + OldName.substr(4).str();
+ }
+};
+
+class EndsWithTatoCheck : public RefactorCheck {
+public:
+ EndsWithTatoCheck(StringRef CheckName, ClangTidyContext *Context)
+ : RefactorCheck(CheckName, Context, "tato$") {}
+
+ std::string newName(StringRef OldName) override {
+ return OldName.substr(0, OldName.size() - 4).str() + "melo";
+ }
+};
+
+} // namespace
+
+TEST(OverlappingReplacementsTest, UseCharCheckTest) {
+ const char Code[] =
+ R"(void f() {
+ int a = 0;
+ if (int b = 0) {
+ int c = a;
+ }
+})";
+
+ const char CharFix[] =
+ R"(void f() {
+ char a = 0;
+ if (char b = 0) {
+ char c = a;
+ }
+})";
+ EXPECT_EQ(CharFix, runCheckOnCode<UseCharCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, IfFalseCheckTest) {
+ const char Code[] =
+ R"(void f() {
+ int potato = 0;
+ if (int b = 0) {
+ int c = potato;
+ } else if (true) {
+ int d = 0;
+ }
+})";
+
+ const char IfFix[] =
+ R"(void f() {
+ int potato = 0;
+ if (false) {
+ int c = potato;
+ } else if (false) {
+ int d = 0;
+ }
+})";
+ EXPECT_EQ(IfFix, runCheckOnCode<IfFalseCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, StartsWithCheckTest) {
+ const char Code[] =
+ R"(void f() {
+ int a = 0;
+ int potato = 0;
+ if (int b = 0) {
+ int c = potato;
+ } else if (true) {
+ int d = 0;
+ }
+})";
+
+ const char StartsFix[] =
+ R"(void f() {
+ int a = 0;
+ int tomato = 0;
+ if (int b = 0) {
+ int c = tomato;
+ } else if (true) {
+ int d = 0;
+ }
+})";
+ EXPECT_EQ(StartsFix, runCheckOnCode<StartsWithPotaCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, EndsWithCheckTest) {
+ const char Code[] =
+ R"(void f() {
+ int a = 0;
+ int potato = 0;
+ if (int b = 0) {
+ int c = potato;
+ } else if (true) {
+ int d = 0;
+ }
+})";
+
+ const char EndsFix[] =
+ R"(void f() {
+ int a = 0;
+ int pomelo = 0;
+ if (int b = 0) {
+ int c = pomelo;
+ } else if (true) {
+ int d = 0;
+ }
+})";
+ EXPECT_EQ(EndsFix, runCheckOnCode<EndsWithTatoCheck>(Code));
+}
+
+TEST(OverlappingReplacementTest, ReplacementsDoNotOverlap) {
+ std::string Res;
+ const char Code[] =
+ R"(void f() {
+ int potassium = 0;
+ if (true) {
+ int Potato = potassium;
+ }
+})";
+
+ const char CharIfFix[] =
+ R"(void f() {
+ char potassium = 0;
+ if (false) {
+ char Potato = potassium;
+ }
+})";
+ Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
+ EXPECT_EQ(CharIfFix, Res);
+
+ const char StartsEndsFix[] =
+ R"(void f() {
+ int tomassium = 0;
+ if (true) {
+ int Pomelo = tomassium;
+ }
+})";
+ Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
+ EXPECT_EQ(StartsEndsFix, Res);
+
+ const char CharIfStartsEndsFix[] =
+ R"(void f() {
+ char tomassium = 0;
+ if (false) {
+ char Pomelo = tomassium;
+ }
+})";
+ Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck,
+ EndsWithTatoCheck>(Code);
+ EXPECT_EQ(CharIfStartsEndsFix, Res);
+}
+
+TEST(OverlappingReplacementsTest, ReplacementInsideOtherReplacement) {
+ std::string Res;
+ const char Code[] =
+ R"(void f() {
+ if (char potato = 0) {
+ } else if (int a = 0) {
+ char potato = 0;
+ if (potato) potato;
+ }
+})";
+
+ // Apply the UseCharCheck together with the IfFalseCheck.
+ //
+ // The 'If' fix contains the other, so that is the one that has to be applied.
+ // } else if (int a = 0) {
+ // ^^^ -> char
+ // ~~~~~~~~~ -> false
+ const char CharIfFix[] =
+ R"(void f() {
+ if (false) {
+ } else if (false) {
+ char potato = 0;
+ if (false) potato;
+ }
+})";
+ Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
+ EXPECT_EQ(CharIfFix, Res);
+ Res = runCheckOnCode<IfFalseCheck, UseCharCheck>(Code);
+ EXPECT_EQ(CharIfFix, Res);
+
+ // Apply the IfFalseCheck with the StartsWithPotaCheck.
+ //
+ // The 'If' replacement is bigger here.
+ // if (char potato = 0) {
+ // ^^^^^^ -> tomato
+ // ~~~~~~~~~~~~~~~ -> false
+ //
+ // But the refactoring is the one that contains the other here:
+ // char potato = 0;
+ // ^^^^^^ -> tomato
+ // if (potato) potato;
+ // ^^^^^^ ^^^^^^ -> tomato, tomato
+ // ~~~~~~ -> false
+ const char IfStartsFix[] =
+ R"(void f() {
+ if (false) {
+ } else if (false) {
+ char tomato = 0;
+ if (tomato) tomato;
+ }
+})";
+ Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
+ EXPECT_EQ(IfStartsFix, Res);
+ Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck>(Code);
+ EXPECT_EQ(IfStartsFix, Res);
+}
+
+TEST(OverlappingReplacements, TwoReplacementsInsideOne) {
+ std::string Res;
+ const char Code[] =
+ R"(void f() {
+ if (int potato = 0) {
+ int a = 0;
+ }
+})";
+
+ // The two smallest replacements should not be applied.
+ // if (int potato = 0) {
+ // ^^^^^^ -> tomato
+ // *** -> char
+ // ~~~~~~~~~~~~~~ -> false
+ // But other errors from the same checks should not be affected.
+ // int a = 0;
+ // *** -> char
+ const char Fix[] =
+ R"(void f() {
+ if (false) {
+ char a = 0;
+ }
+})";
+ Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck>(Code);
+ EXPECT_EQ(Fix, Res);
+ Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck, UseCharCheck>(Code);
+ EXPECT_EQ(Fix, Res);
+}
+
+TEST(OverlappingReplacementsTest,
+ ApplyAtMostOneOfTheChangesWhenPartialOverlapping) {
+ std::string Res;
+ const char Code[] =
+ R"(void f() {
+ if (int potato = 0) {
+ int a = potato;
+ }
+})";
+
+ // These two replacements overlap, but none of them is completely contained
+ // inside the other.
+ // if (int potato = 0) {
+ // ^^^^^^ -> tomato
+ // ~~~~~~~~~~~~~~ -> false
+ // int a = potato;
+ // ^^^^^^ -> tomato
+ //
+ // The 'StartsWithPotaCheck' fix has endpoints inside the 'IfFalseCheck' fix,
+ // so it is going to be set as inapplicable. The 'if' fix will be applied.
+ const char IfFix[] =
+ R"(void f() {
+ if (false) {
+ int a = potato;
+ }
+})";
+ Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
+ EXPECT_EQ(IfFix, Res);
+}
+
+TEST(OverlappingReplacementsTest, TwoErrorsHavePerfectOverlapping) {
+ std::string Res;
+ const char Code[] =
+ R"(void f() {
+ int potato = 0;
+ potato += potato * potato;
+ if (char a = potato) potato;
+})";
+
+ // StartsWithPotaCheck will try to refactor 'potato' into 'tomato', and
+ // EndsWithTatoCheck will try to use 'pomelo'. Both fixes have the same set of
+ // ranges. This is a corner case of one error completely containing another:
+ // the other completely contains the first one as well. Both errors are
+ // discarded.
+
+ Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
+ EXPECT_EQ(Code, Res);
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+#include "ClangTidyTest.h"
+#include "readability/BracesAroundStatementsCheck.h"
+#include "readability/NamespaceCommentCheck.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using readability::BracesAroundStatementsCheck;
+using readability::NamespaceCommentCheck;
+
+TEST(NamespaceCommentCheckTest, Basic) {
+ EXPECT_EQ("namespace i {\n} // namespace i",
+ runCheckOnCode<NamespaceCommentCheck>("namespace i {\n}"));
+ EXPECT_EQ("namespace {\n} // namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n}"));
+ EXPECT_EQ("namespace i { namespace j {\n} // namespace j\n } // namespace i",
+ runCheckOnCode<NamespaceCommentCheck>(
+ "namespace i { namespace j {\n} }"));
+}
+
+TEST(NamespaceCommentCheckTest, SingleLineNamespaces) {
+ EXPECT_EQ(
+ "namespace i { namespace j { } }",
+ runCheckOnCode<NamespaceCommentCheck>("namespace i { namespace j { } }"));
+}
+
+TEST(NamespaceCommentCheckTest, CheckExistingComments) {
+ EXPECT_EQ("namespace i { namespace j {\n"
+ "} /* namespace j */ } // namespace i\n"
+ " /* random comment */",
+ runCheckOnCode<NamespaceCommentCheck>(
+ "namespace i { namespace j {\n"
+ "} /* namespace j */ } /* random comment */"));
+ EXPECT_EQ("namespace {\n"
+ "} // namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} // namespace"));
+ EXPECT_EQ("namespace {\n"
+ "} //namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} //namespace"));
+ EXPECT_EQ("namespace {\n"
+ "} // anonymous namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} // anonymous namespace"));
+ EXPECT_EQ("namespace {\n"
+ "} // Anonymous namespace.",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} // Anonymous namespace."));
+ EXPECT_EQ(
+ "namespace q {\n"
+ "} // namespace q",
+ runCheckOnCode<NamespaceCommentCheck>("namespace q {\n"
+ "} // anonymous namespace q"));
+ EXPECT_EQ(
+ "namespace My_NameSpace123 {\n"
+ "} // namespace My_NameSpace123",
+ runCheckOnCode<NamespaceCommentCheck>("namespace My_NameSpace123 {\n"
+ "} // namespace My_NameSpace123"));
+ EXPECT_EQ(
+ "namespace My_NameSpace123 {\n"
+ "} //namespace My_NameSpace123",
+ runCheckOnCode<NamespaceCommentCheck>("namespace My_NameSpace123 {\n"
+ "} //namespace My_NameSpace123"));
+ EXPECT_EQ("namespace My_NameSpace123 {\n"
+ "} // end namespace My_NameSpace123",
+ runCheckOnCode<NamespaceCommentCheck>(
+ "namespace My_NameSpace123 {\n"
+ "} // end namespace My_NameSpace123"));
+ // Understand comments only on the same line.
+ EXPECT_EQ("namespace {\n"
+ "} // namespace\n"
+ "// namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "}\n"
+ "// namespace"));
+}
+
+TEST(NamespaceCommentCheckTest, FixWrongComments) {
+ EXPECT_EQ("namespace i { namespace jJ0_ {\n"
+ "} // namespace jJ0_\n"
+ " } // namespace i\n"
+ " /* random comment */",
+ runCheckOnCode<NamespaceCommentCheck>(
+ "namespace i { namespace jJ0_ {\n"
+ "} /* namespace qqq */ } /* random comment */"));
+ EXPECT_EQ("namespace {\n"
+ "} // namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} // namespace asdf"));
+ // Remove unknown line comments. These are likely to be an unrecognized form
+ // of a namespace ending comment.
+ EXPECT_EQ("namespace {\n"
+ "} // namespace",
+ runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+ "} // random text"));
+}
+
+TEST(BracesAroundStatementsCheckTest, IfWithComments) {
+ EXPECT_EQ("int main() {\n"
+ " if (false /*dummy token*/) {\n"
+ " // comment\n"
+ " return -1; /**/\n"
+ "}\n"
+ " if (false) {\n"
+ " return -1; // comment\n"
+ "}\n"
+ " if (false) {\n"
+ " return -1; \n"
+ "}/* multi-line \n comment */\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (false /*dummy token*/)\n"
+ " // comment\n"
+ " return -1; /**/\n"
+ " if (false)\n"
+ " return -1; // comment\n"
+ " if (false)\n"
+ " return -1; /* multi-line \n comment */\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " if (false /*dummy token*/) {\n"
+ " // comment\n"
+ " return -1 /**/ ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (false /*dummy token*/)\n"
+ " // comment\n"
+ " return -1 /**/ ;\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, If) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ " }\n"
+ "}");
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " if (auto Cond = false) {\n"
+ " return -1;\n"
+ " }\n"
+ "}");
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ " } else {\n"
+ " return -2;\n"
+ " }\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " if (false)\n"
+ " return -1;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " if (auto Cond = false /**/ ) {\n"
+ " return -1;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (auto Cond = false /**/ )\n"
+ " return -1;\n"
+ "}"));
+ // FIXME: Consider adding braces before EMPTY_MACRO and after the statement.
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+ "#define EMPTY_MACRO\n"
+ "int main() {\n"
+ " if (auto Cond = false EMPTY_MACRO /**/ ) EMPTY_MACRO\n"
+ " return -1;\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " if (true) { return -1/**/ ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (true) return -1/**/ ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ " } else {\n"
+ " return -2;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " if (false)\n"
+ " return -1;\n"
+ " else\n"
+ " return -2;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ " } else if (1 == 2) {\n"
+ " return -2;\n"
+ " } else {\n"
+ " return -3;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " if (false)\n"
+ " return -1;\n"
+ " else if (1 == 2)\n"
+ " return -2;\n"
+ " else\n"
+ " return -3;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " if (false) {\n"
+ " return -1;\n"
+ " } else if (1 == 2) {\n"
+ " return -2;\n"
+ " } else {\n"
+ " return -3;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " if (false)\n"
+ " return -1;\n"
+ " else if (1 == 2) {\n"
+ " return -2;\n"
+ " } else\n"
+ " return -3;\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, IfElseWithShortStatements) {
+ ClangTidyOptions Options;
+ Options.CheckOptions["test-check-0.ShortStatementLines"] = "1";
+
+ EXPECT_EQ("int main() {\n"
+ " if (true) return 1;\n"
+ " if (false) { return -1;\n"
+ " } else if (1 == 2) { return -2;\n"
+ " } else { return -3;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (true) return 1;\n"
+ " if (false) return -1;\n"
+ " else if (1 == 2) return -2;\n"
+ " else return -3;\n"
+ "}",
+ nullptr, "input.cc", None, Options));
+
+ // If the last else is an else-if, we also force it.
+ EXPECT_EQ("int main() {\n"
+ " if (false) { return -1;\n"
+ " } else if (1 == 2) { return -2;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " if (false) return -1;\n"
+ " else if (1 == 2) return -2;\n"
+ "}",
+ nullptr, "input.cc", None, Options));
+}
+
+TEST(BracesAroundStatementsCheckTest, For) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " for (;;) {\n"
+ " ;\n"
+ " }\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " for (;;) {\n"
+ " ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " for (;;)\n"
+ " ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " for (;;) {\n"
+ " /**/ ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " for (;;)\n"
+ " /**/ ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " for (;;) {\n"
+ " return -1 /**/ ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " for (;;)\n"
+ " return -1 /**/ ;\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, ForRange) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " int arr[4];\n"
+ " for (int i : arr) {\n"
+ " ;\n"
+ " }\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " int arr[4];\n"
+ " for (int i : arr) {\n"
+ " ;\n"
+ "}\n"
+ " for (int i : arr) {\n"
+ " return -1 ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " int arr[4];\n"
+ " for (int i : arr)\n"
+ " ;\n"
+ " for (int i : arr)\n"
+ " return -1 ;\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, DoWhile) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " do {\n"
+ " ;\n"
+ " } while (false);\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " do {\n"
+ " ;\n"
+ " } while (false);\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " do\n"
+ " ;\n"
+ " while (false);\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, While) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+ " while (false) {\n"
+ " ;\n"
+ " }\n"
+ "}");
+ EXPECT_EQ("int main() {\n"
+ " while (false) {\n"
+ " ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " while (false)\n"
+ " ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " while (auto Cond = false) {\n"
+ " ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " while (auto Cond = false)\n"
+ " ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " while (false /*dummy token*/) {\n"
+ " ;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " while (false /*dummy token*/)\n"
+ " ;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " while (false) {\n"
+ " break;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " while (false)\n"
+ " break;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " while (false) {\n"
+ " break /**/;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " while (false)\n"
+ " break /**/;\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " while (false) {\n"
+ " /**/;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " while (false)\n"
+ " /**/;\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, Nested) {
+ EXPECT_EQ("int main() {\n"
+ " do { if (true) {}} while (false);\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " do if (true) {}while (false);\n"
+ "}"));
+ EXPECT_EQ("int main() {\n"
+ " do { if (true) {}} while (false);\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>(
+ "int main() {\n"
+ " do if (true) {}while (false);\n"
+ "}"));
+ EXPECT_EQ(
+ "int main() {\n"
+ " if (true) {\n"
+ " // comment\n"
+ " if (false) {\n"
+ " // comment\n"
+ " /**/ ; // comment\n"
+ " }\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+ " if (true)\n"
+ " // comment\n"
+ " if (false) {\n"
+ " // comment\n"
+ " /**/ ; // comment\n"
+ " }\n"
+ "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, Macros) {
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+ "#define IF(COND) if (COND) return -1;\n"
+ "int main() {\n"
+ " IF(false)\n"
+ "}");
+ EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+ "#define FOR(COND) for (COND) return -1;\n"
+ "int main() {\n"
+ " FOR(;;)\n"
+ "}");
+ EXPECT_EQ("#define DO_IT ++i\n"
+ "int i = 0;\n"
+ "int main() {\n"
+ " if (false) {\n"
+ " DO_IT;\n"
+ " } else if (1 == 2) {\n"
+ " DO_IT;\n"
+ " } else {\n"
+ " DO_IT;\n"
+ "}\n"
+ "}",
+ runCheckOnCode<BracesAroundStatementsCheck>("#define DO_IT ++i\n"
+ "int i = 0;\n"
+ "int main() {\n"
+ " if (false)\n"
+ " DO_IT;\n"
+ " else if (1 == 2)\n"
+ " DO_IT;\n"
+ " else\n"
+ " DO_IT;\n"
+ "}"));
+}
+
+#define EXPECT_NO_CHANGES_WITH_OPTS(Check, Opts, Code) \
+ EXPECT_EQ(Code, runCheckOnCode<Check>(Code, nullptr, "input.cc", None, Opts))
+TEST(BracesAroundStatementsCheckTest, ImplicitCastInReturn) {
+ ClangTidyOptions Opts;
+ Opts.CheckOptions["test-check-0.ShortStatementLines"] = "1";
+
+ EXPECT_NO_CHANGES_WITH_OPTS(BracesAroundStatementsCheck, Opts,
+ "const char *f() {\n"
+ " if (true) return \"\";\n"
+ " return \"abc\";\n"
+ "}\n");
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
--- /dev/null
+//===---- UsingInserterTest.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 "../clang-tidy/utils/UsingInserter.h"
+
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// Replace all function calls with calls to foo::func. Inserts using
+// declarations as necessary. This checker is for testing only. It
+// can only run on one test case (e.g. wih one SourceManager).
+class InsertUsingCheck : public clang::tidy::ClangTidyCheck {
+public:
+ InsertUsingCheck(StringRef Name, ClangTidyContext *Context)
+ :ClangTidyCheck(Name, Context) {}
+ void registerMatchers(clang::ast_matchers::MatchFinder *Finder) override {
+ Finder->addMatcher(clang::ast_matchers::callExpr().bind("foo"), this);
+ }
+ void
+ check(const clang::ast_matchers::MatchFinder::MatchResult &Result) override {
+ if (!Inserter)
+ Inserter.reset(new UsingInserter(*Result.SourceManager));
+
+ const auto *Call = Result.Nodes.getNodeAs<clang::CallExpr>("foo");
+ assert(Call != nullptr && "Did not find node \"foo\"");
+ auto Hint =
+ Inserter->createUsingDeclaration(*Result.Context, *Call, "::foo::func");
+
+ if (Hint.hasValue())
+ diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
+
+ diag(Call->getLocStart(), "insert call")
+ << clang::FixItHint::CreateReplacement(
+ Call->getCallee()->getSourceRange(),
+ Inserter->getShortName(*Result.Context, *Call, "::foo::func"));
+ }
+
+private:
+ std::unique_ptr<UsingInserter> Inserter;
+};
+
+template <typename Check>
+std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
+ std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
+ "namespace foo {\n"
+ "namespace bar {\n"
+ "}\n"
+ "void func() { }\n"
+ "}"}};
+ std::vector<ClangTidyError> errors;
+
+ std::string result =
+ test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
+ ClangTidyOptions(), AdditionalFileContents);
+
+ EXPECT_EQ(ExpectedWarningCount, errors.size());
+ return result;
+}
+
+TEST(UsingInserterTest, ReusesExisting) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "namespace {"
+ "using ::foo::func;\n"
+ "void f() { func(); }"
+ "}",
+ runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+ "namespace {"
+ "using ::foo::func;\n"
+ "void f() { f(); }"
+ "}",
+ 1));
+}
+
+TEST(UsingInserterTest, ReusesExistingGlobal) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "using ::foo::func;\n"
+ "namespace {"
+ "void f() { func(); }"
+ "}",
+ runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+ "using ::foo::func;\n"
+ "namespace {"
+ "void f() { f(); }"
+ "}",
+ 1));
+}
+
+TEST(UsingInserterTest, AvoidsConflict) {
+ EXPECT_EQ("#include \"foo.h\"\n"
+ "namespace {"
+ "void f() { int func; ::foo::func(); }"
+ "}",
+ runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+ "namespace {"
+ "void f() { int func; f(); }"
+ "}",
+ 1));
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(CLANGD_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+ ${CLANGD_SOURCE_DIR}
+ )
+
+add_extra_unittest(ClangdTests
+ ClangdTests.cpp
+ )
+
+target_link_libraries(ClangdTests
+ clangBasic
+ clangDaemon
+ clangFormat
+ clangFrontend
+ clangSema
+ clangTooling
+ clangToolingCore
+ LLVMSupport
+ )
--- /dev/null
+//===-- ClangdTests.cpp - Clangd unit tests ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Config/config.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace vfs {
+
+/// An implementation of vfs::FileSystem that only allows access to
+/// files and folders inside a set of whitelisted directories.
+///
+/// FIXME(ibiryukov): should it also emulate access to parents of whitelisted
+/// directories with only whitelisted contents?
+class FilteredFileSystem : public vfs::FileSystem {
+public:
+ /// The paths inside \p WhitelistedDirs should be absolute
+ FilteredFileSystem(std::vector<std::string> WhitelistedDirs,
+ IntrusiveRefCntPtr<vfs::FileSystem> InnerFS)
+ : WhitelistedDirs(std::move(WhitelistedDirs)), InnerFS(InnerFS) {
+ assert(std::all_of(WhitelistedDirs.begin(), WhitelistedDirs.end(),
+ [](const std::string &Path) -> bool {
+ return llvm::sys::path::is_absolute(Path);
+ }) &&
+ "Not all WhitelistedDirs are absolute");
+ }
+
+ virtual llvm::ErrorOr<Status> status(const Twine &Path) {
+ if (!isInsideWhitelistedDir(Path))
+ return llvm::errc::no_such_file_or_directory;
+ return InnerFS->status(Path);
+ }
+
+ virtual llvm::ErrorOr<std::unique_ptr<File>>
+ openFileForRead(const Twine &Path) {
+ if (!isInsideWhitelistedDir(Path))
+ return llvm::errc::no_such_file_or_directory;
+ return InnerFS->openFileForRead(Path);
+ }
+
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+ getBufferForFile(const Twine &Name, int64_t FileSize = -1,
+ bool RequiresNullTerminator = true,
+ bool IsVolatile = false) {
+ if (!isInsideWhitelistedDir(Name))
+ return llvm::errc::no_such_file_or_directory;
+ return InnerFS->getBufferForFile(Name, FileSize, RequiresNullTerminator,
+ IsVolatile);
+ }
+
+ virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) {
+ if (!isInsideWhitelistedDir(Dir)) {
+ EC = llvm::errc::no_such_file_or_directory;
+ return directory_iterator();
+ }
+ return InnerFS->dir_begin(Dir, EC);
+ }
+
+ virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) {
+ return InnerFS->setCurrentWorkingDirectory(Path);
+ }
+
+ virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const {
+ return InnerFS->getCurrentWorkingDirectory();
+ }
+
+ bool exists(const Twine &Path) {
+ if (!isInsideWhitelistedDir(Path))
+ return false;
+ return InnerFS->exists(Path);
+ }
+
+ std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const {
+ return InnerFS->makeAbsolute(Path);
+ }
+
+private:
+ bool isInsideWhitelistedDir(const Twine &InputPath) const {
+ SmallString<128> Path;
+ InputPath.toVector(Path);
+
+ if (makeAbsolute(Path))
+ return false;
+
+ for (const auto &Dir : WhitelistedDirs) {
+ if (Path.startswith(Dir))
+ return true;
+ }
+ return false;
+ }
+
+ std::vector<std::string> WhitelistedDirs;
+ IntrusiveRefCntPtr<vfs::FileSystem> InnerFS;
+};
+
+/// Create a vfs::FileSystem that has access only to temporary directories
+/// (obtained by calling system_temp_directory).
+IntrusiveRefCntPtr<vfs::FileSystem> getTempOnlyFS() {
+ llvm::SmallString<128> TmpDir1;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, TmpDir1);
+ llvm::SmallString<128> TmpDir2;
+ llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true, TmpDir2);
+
+ std::vector<std::string> TmpDirs;
+ TmpDirs.push_back(TmpDir1.str());
+ if (TmpDir1 != TmpDir2)
+ TmpDirs.push_back(TmpDir2.str());
+ return new vfs::FilteredFileSystem(std::move(TmpDirs),
+ vfs::getRealFileSystem());
+}
+} // namespace vfs
+
+namespace clangd {
+namespace {
+
+class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
+public:
+ void onDiagnosticsReady(PathRef File,
+ Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
+ bool HadError = false;
+ for (const auto &DiagAndFixIts : Diagnostics.Value) {
+ // FIXME: severities returned by clangd should have a descriptive
+ // diagnostic severity enum
+ const int ErrorSeverity = 1;
+ HadError = DiagAndFixIts.Diag.severity == ErrorSeverity;
+ }
+
+ std::lock_guard<std::mutex> Lock(Mutex);
+ HadErrorInLastDiags = HadError;
+ LastVFSTag = Diagnostics.Tag;
+ }
+
+ bool hadErrorInLastDiags() {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ return HadErrorInLastDiags;
+ }
+
+ VFSTag lastVFSTag() {
+ return LastVFSTag;
+ }
+
+private:
+ std::mutex Mutex;
+ bool HadErrorInLastDiags = false;
+ VFSTag LastVFSTag = VFSTag();
+};
+
+class MockCompilationDatabase : public GlobalCompilationDatabase {
+public:
+ MockCompilationDatabase(bool AddFreestandingFlag) {
+ // We have to add -ffreestanding to VFS-specific tests to avoid errors on
+ // implicit includes of stdc-predef.h.
+ if (AddFreestandingFlag)
+ ExtraClangFlags.push_back("-ffreestanding");
+ }
+
+ std::vector<tooling::CompileCommand>
+ getCompileCommands(PathRef File) override {
+ if (ExtraClangFlags.empty())
+ return {};
+
+ std::vector<std::string> CommandLine;
+ CommandLine.reserve(3 + ExtraClangFlags.size());
+ CommandLine.insert(CommandLine.end(), {"clang", "-fsyntax-only"});
+ CommandLine.insert(CommandLine.end(), ExtraClangFlags.begin(),
+ ExtraClangFlags.end());
+ CommandLine.push_back(File.str());
+
+ return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
+ llvm::sys::path::filename(File),
+ CommandLine, "")};
+ }
+
+ std::vector<std::string> ExtraClangFlags;
+};
+
+class MockFSProvider : public FileSystemProvider {
+public:
+ Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+ getTaggedFileSystem(PathRef File) override {
+ IntrusiveRefCntPtr<vfs::InMemoryFileSystem> MemFS(
+ new vfs::InMemoryFileSystem);
+ if (ExpectedFile)
+ EXPECT_EQ(*ExpectedFile, File);
+
+ for (auto &FileAndContents : Files)
+ MemFS->addFile(FileAndContents.first(), time_t(),
+ llvm::MemoryBuffer::getMemBuffer(FileAndContents.second,
+ FileAndContents.first()));
+
+ auto OverlayFS = IntrusiveRefCntPtr<vfs::OverlayFileSystem>(
+ new vfs::OverlayFileSystem(vfs::getTempOnlyFS()));
+ OverlayFS->pushOverlay(std::move(MemFS));
+ return make_tagged(OverlayFS, Tag);
+ }
+
+ llvm::Optional<SmallString<32>> ExpectedFile;
+ llvm::StringMap<std::string> Files;
+ VFSTag Tag = VFSTag();
+};
+
+/// Replaces all patterns of the form 0x123abc with spaces
+std::string replacePtrsInDump(std::string const &Dump) {
+ llvm::Regex RE("0x[0-9a-fA-F]+");
+ llvm::SmallVector<StringRef, 1> Matches;
+ llvm::StringRef Pending = Dump;
+
+ std::string Result;
+ while (RE.match(Pending, &Matches)) {
+ assert(Matches.size() == 1 && "Exactly one match expected");
+ auto MatchPos = Matches[0].data() - Pending.data();
+
+ Result += Pending.take_front(MatchPos);
+ Pending = Pending.drop_front(MatchPos + Matches[0].size());
+ }
+ Result += Pending;
+
+ return Result;
+}
+
+std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
+ auto DumpWithMemLocs = Server.dumpAST(File);
+ return replacePtrsInDump(DumpWithMemLocs);
+}
+
+} // namespace
+
+class ClangdVFSTest : public ::testing::Test {
+protected:
+ SmallString<16> getVirtualTestRoot() {
+#ifdef LLVM_ON_WIN32
+ return SmallString<16>("C:\\clangd-test");
+#else
+ return SmallString<16>("/clangd-test");
+#endif
+ }
+
+ llvm::SmallString<32> getVirtualTestFilePath(PathRef File) {
+ assert(llvm::sys::path::is_relative(File) && "FileName should be relative");
+
+ llvm::SmallString<32> Path;
+ llvm::sys::path::append(Path, getVirtualTestRoot(), File);
+ return Path;
+ }
+
+ std::string parseSourceAndDumpAST(
+ PathRef SourceFileRelPath, StringRef SourceContents,
+ std::vector<std::pair<PathRef, StringRef>> ExtraFiles = {},
+ bool ExpectErrors = false) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/false);
+ for (const auto &FileWithContents : ExtraFiles)
+ FS.Files[getVirtualTestFilePath(FileWithContents.first)] =
+ FileWithContents.second;
+
+ auto SourceFilename = getVirtualTestFilePath(SourceFileRelPath);
+
+ FS.ExpectedFile = SourceFilename;
+ Server.addDocument(SourceFilename, SourceContents);
+
+ auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename);
+ EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
+ return Result;
+ }
+};
+
+TEST_F(ClangdVFSTest, Parse) {
+ // FIXME: figure out a stable format for AST dumps, so that we can check the
+ // output of the dump itself is equal to the expected one, not just that it's
+ // different.
+ auto Empty = parseSourceAndDumpAST("foo.cpp", "", {});
+ auto OneDecl = parseSourceAndDumpAST("foo.cpp", "int a;", {});
+ auto SomeDecls = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;", {});
+ EXPECT_NE(Empty, OneDecl);
+ EXPECT_NE(Empty, SomeDecls);
+ EXPECT_NE(SomeDecls, OneDecl);
+
+ auto Empty2 = parseSourceAndDumpAST("foo.cpp", "");
+ auto OneDecl2 = parseSourceAndDumpAST("foo.cpp", "int a;");
+ auto SomeDecls2 = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
+ EXPECT_EQ(Empty, Empty2);
+ EXPECT_EQ(OneDecl, OneDecl2);
+ EXPECT_EQ(SomeDecls, SomeDecls2);
+}
+
+TEST_F(ClangdVFSTest, ParseWithHeader) {
+ parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {},
+ /*ExpectErrors=*/true);
+ parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {{"foo.h", ""}},
+ /*ExpectErrors=*/false);
+
+ const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+ parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", ""}},
+ /*ExpectErrors=*/true);
+ parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", "int a;"}},
+ /*ExpectErrors=*/false);
+}
+
+TEST_F(ClangdVFSTest, Reparse) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/false);
+
+ const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ auto FooH = getVirtualTestFilePath("foo.h");
+
+ FS.Files[FooH] = "int a;";
+ FS.Files[FooCpp] = SourceContents;
+ FS.ExpectedFile = FooCpp;
+
+ Server.addDocument(FooCpp, SourceContents);
+ auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ Server.addDocument(FooCpp, "");
+ auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ Server.addDocument(FooCpp, SourceContents);
+ auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ EXPECT_EQ(DumpParse1, DumpParse2);
+ EXPECT_NE(DumpParse1, DumpParseEmpty);
+}
+
+TEST_F(ClangdVFSTest, ReparseOnHeaderChange) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/false);
+
+ const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ auto FooH = getVirtualTestFilePath("foo.h");
+
+ FS.Files[FooH] = "int a;";
+ FS.Files[FooCpp] = SourceContents;
+ FS.ExpectedFile = FooCpp;
+
+ Server.addDocument(FooCpp, SourceContents);
+ auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ FS.Files[FooH] = "";
+ Server.forceReparse(FooCpp);
+ auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
+
+ FS.Files[FooH] = "int a;";
+ Server.forceReparse(FooCpp);
+ auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ EXPECT_EQ(DumpParse1, DumpParse2);
+ EXPECT_NE(DumpParse1, DumpParseDifferent);
+}
+
+TEST_F(ClangdVFSTest, CheckVersions) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/true);
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ const auto SourceContents = "int a;";
+ FS.Files[FooCpp] = SourceContents;
+ FS.ExpectedFile = FooCpp;
+
+ FS.Tag = "123";
+ Server.addDocument(FooCpp, SourceContents);
+ EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag);
+ EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}).Tag, FS.Tag);
+
+ FS.Tag = "321";
+ Server.addDocument(FooCpp, SourceContents);
+ EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag);
+ EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}).Tag, FS.Tag);
+}
+
+// Only enable this test on Unix
+#ifdef LLVM_ON_UNIX
+TEST_F(ClangdVFSTest, SearchLibDir) {
+ // Checks that searches for GCC installation is done through vfs.
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+ CDB.ExtraClangFlags.insert(CDB.ExtraClangFlags.end(),
+ {"-xc++", "-target", "x86_64-linux-unknown",
+ "-m64", "-stdlib=libstdc++"});
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/true);
+
+ // Just a random gcc version string
+ SmallString<8> Version("4.9.3");
+
+ // A lib dir for gcc installation
+ SmallString<64> LibDir("/usr/lib/gcc/x86_64-linux-gnu");
+ llvm::sys::path::append(LibDir, Version);
+
+ // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
+ // installation there.
+ SmallString<64> DummyLibFile;
+ llvm::sys::path::append(DummyLibFile, LibDir, "64", "crtbegin.o");
+ FS.Files[DummyLibFile] = "";
+
+ SmallString<64> IncludeDir("/usr/include/c++");
+ llvm::sys::path::append(IncludeDir, Version);
+
+ SmallString<64> StringPath;
+ llvm::sys::path::append(StringPath, IncludeDir, "string");
+ FS.Files[StringPath] = "class mock_string {};";
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ const auto SourceContents = R"cpp(
+#include <string>
+mock_string x;
+)cpp";
+ FS.Files[FooCpp] = SourceContents;
+
+ Server.addDocument(FooCpp, SourceContents);
+ EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+ const auto SourceContentsWithError = R"cpp(
+#include <string>
+std::string x;
+)cpp";
+ Server.addDocument(FooCpp, SourceContentsWithError);
+ EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
+}
+#endif // LLVM_ON_UNIX
+
+class ClangdCompletionTest : public ClangdVFSTest {
+protected:
+ bool ContainsItem(std::vector<CompletionItem> const &Items, StringRef Name) {
+ for (const auto &Item : Items) {
+ if (Item.insertText == Name)
+ return true;
+ }
+ return false;
+ }
+};
+
+TEST_F(ClangdCompletionTest, CheckContentsOverride) {
+ MockFSProvider FS;
+ ErrorCheckingDiagConsumer DiagConsumer;
+ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+
+ ClangdServer Server(CDB, DiagConsumer, FS,
+ /*RunSynchronously=*/false);
+
+ auto FooCpp = getVirtualTestFilePath("foo.cpp");
+ const auto SourceContents = R"cpp(
+int aba;
+int b = ;
+)cpp";
+
+ const auto OverridenSourceContents = R"cpp(
+int cbc;
+int b = ;
+)cpp";
+ // Complete after '=' sign. We need to be careful to keep the SourceContents'
+ // size the same.
+ // We complete on the 3rd line (2nd in zero-based numbering), because raw
+ // string literal of the SourceContents starts with a newline(it's easy to
+ // miss).
+ Position CompletePos = {2, 8};
+ FS.Files[FooCpp] = SourceContents;
+ FS.ExpectedFile = FooCpp;
+
+ Server.addDocument(FooCpp, SourceContents);
+
+ {
+ auto CodeCompletionResults1 =
+ Server.codeComplete(FooCpp, CompletePos, None).Value;
+ EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba"));
+ EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc"));
+ }
+
+ {
+ auto CodeCompletionResultsOverriden =
+ Server
+ .codeComplete(FooCpp, CompletePos,
+ StringRef(OverridenSourceContents))
+ .Value;
+ EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc"));
+ EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba"));
+ }
+
+ {
+ auto CodeCompletionResults2 =
+ Server.codeComplete(FooCpp, CompletePos, None).Value;
+ EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba"));
+ EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc"));
+ }
+}
+
+} // namespace clangd
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../include-fixer REALPATH)
+include_directories(
+ ${INCLUDE_FIXER_SOURCE_DIR}
+ )
+
+# We'd like to clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(IncludeFixerTests
+ IncludeFixerTest.cpp
+ FuzzySymbolIndexTests.cpp
+ )
+
+target_link_libraries(IncludeFixerTests
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangIncludeFixer
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ findAllSymbols
+ )
+
+add_subdirectory(find-all-symbols)
--- /dev/null
+//===-- FuzzySymbolIndexTests.cpp - Fuzzy symbol index unit tests ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzySymbolIndex.h"
+#include "gmock/gmock.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::Not;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+TEST(FuzzySymbolIndexTest, Tokenize) {
+ EXPECT_THAT(FuzzySymbolIndex::tokenize("URLHandlerCallback"),
+ ElementsAre("url", "handler", "callback"));
+ EXPECT_THAT(FuzzySymbolIndex::tokenize("snake_case11"),
+ ElementsAre("snake", "case", "11"));
+ EXPECT_THAT(FuzzySymbolIndex::tokenize("__$42!!BOB\nbob"),
+ ElementsAre("42", "bob", "bob"));
+}
+
+MATCHER_P(MatchesSymbol, Identifier, "") {
+ llvm::Regex Pattern("^" + arg);
+ std::string err;
+ if (!Pattern.isValid(err)) {
+ *result_listener << "invalid regex: " << err;
+ return false;
+ }
+ auto Tokens = FuzzySymbolIndex::tokenize(Identifier);
+ std::string Target = llvm::join(Tokens.begin(), Tokens.end(), " ");
+ *result_listener << "matching against '" << Target << "'";
+ return llvm::Regex("^" + arg).match(Target);
+}
+
+TEST(FuzzySymbolIndexTest, QueryRegexp) {
+ auto QueryRegexp = [](const std::string &query) {
+ return FuzzySymbolIndex::queryRegexp(FuzzySymbolIndex::tokenize(query));
+ };
+ EXPECT_THAT(QueryRegexp("uhc"), MatchesSymbol("URLHandlerCallback"));
+ EXPECT_THAT(QueryRegexp("urhaca"), MatchesSymbol("URLHandlerCallback"));
+ EXPECT_THAT(QueryRegexp("uhcb"), Not(MatchesSymbol("URLHandlerCallback")))
+ << "Non-prefix";
+ EXPECT_THAT(QueryRegexp("uc"), Not(MatchesSymbol("URLHandlerCallback")))
+ << "Skip token";
+
+ EXPECT_THAT(QueryRegexp("uptr"), MatchesSymbol("unique_ptr"));
+ EXPECT_THAT(QueryRegexp("UniP"), MatchesSymbol("unique_ptr"));
+}
+
+} // namespace
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+//===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemorySymbolIndex.h"
+#include "IncludeFixer.h"
+#include "SymbolIndexManager.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+using find_all_symbols::SymbolInfo;
+using find_all_symbols::SymbolAndSignals;
+
+static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,
+ StringRef FileName,
+ const std::vector<std::string> &ExtraArgs) {
+ llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+ new vfs::InMemoryFileSystem);
+ llvm::IntrusiveRefCntPtr<FileManager> Files(
+ new FileManager(FileSystemOptions(), InMemoryFileSystem));
+ // FIXME: Investigate why -fms-compatibility breaks tests.
+ std::vector<std::string> Args = {"include_fixer", "-fsyntax-only",
+ "-fno-ms-compatibility", FileName};
+ Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
+ tooling::ToolInvocation Invocation(
+ Args, ToolAction, Files.get(),
+ std::make_shared<PCHContainerOperations>());
+
+ InMemoryFileSystem->addFile(FileName, 0,
+ llvm::MemoryBuffer::getMemBuffer(Code));
+
+ InMemoryFileSystem->addFile("foo.h", 0,
+ llvm::MemoryBuffer::getMemBuffer("\n"));
+ InMemoryFileSystem->addFile("dir/bar.h", 0,
+ llvm::MemoryBuffer::getMemBuffer("\n"));
+ InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0,
+ llvm::MemoryBuffer::getMemBuffer("\n"));
+ InMemoryFileSystem->addFile("header.h", 0,
+ llvm::MemoryBuffer::getMemBuffer("bar b;"));
+ return Invocation.run();
+}
+
+static std::string runIncludeFixer(
+ StringRef Code,
+ const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
+ std::vector<SymbolAndSignals> Symbols = {
+ {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>",
+ {{SymbolInfo::ContextType::Namespace, "std"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"",
+ {{SymbolInfo::ContextType::Namespace, "std"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("foo", SymbolInfo::SymbolKind::Class,
+ "\"dir/otherdir/qux.h\"",
+ {{SymbolInfo::ContextType::Namespace, "b"},
+ {SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",
+ {{SymbolInfo::ContextType::Namespace, "b"},
+ {SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"",
+ {{SymbolInfo::ContextType::Namespace, "c"},
+ {SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"",
+ {{SymbolInfo::ContextType::EnumDecl, "Color"},
+ {SymbolInfo::ContextType::Namespace, "b"},
+ {SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
+ {{SymbolInfo::ContextType::Namespace, "__a"},
+ {SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{/*Seen=*/2, 0}},
+ {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
+ {{SymbolInfo::ContextType::Namespace, "a"}}),
+ SymbolInfo::Signals{/*Seen=*/2, 0}},
+ {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",
+ {{SymbolInfo::ContextType::Namespace, "str"}}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}),
+ SymbolInfo::Signals{}},
+ {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}),
+ SymbolInfo::Signals{}},
+ };
+ auto SymbolIndexMgr = llvm::make_unique<SymbolIndexManager>();
+ SymbolIndexMgr->addSymbolIndex(
+ [=]() { return llvm::make_unique<InMemorySymbolIndex>(Symbols); });
+
+ std::vector<IncludeFixerContext> FixerContexts;
+ IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm");
+ std::string FakeFileName = "input.cc";
+ runOnCode(&Factory, Code, FakeFileName, ExtraArgs);
+ assert(FixerContexts.size() == 1);
+ if (FixerContexts.front().getHeaderInfos().empty())
+ return Code;
+ auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front());
+ EXPECT_TRUE(static_cast<bool>(Replaces))
+ << llvm::toString(Replaces.takeError()) << "\n";
+ if (!Replaces)
+ return "";
+ RewriterTestContext Context;
+ FileID ID = Context.createInMemoryFile(FakeFileName, Code);
+ tooling::applyAllReplacements(*Replaces, Context.Rewrite);
+ return Context.getRewrittenText(ID);
+}
+
+TEST(IncludeFixer, Typo) {
+ EXPECT_EQ("#include <string>\nstd::string foo;\n",
+ runIncludeFixer("std::string foo;\n"));
+
+ EXPECT_EQ("// comment\n#include \"foo.h\"\n#include <string>\n"
+ "std::string foo;\n#include \"dir/bar.h\"\n",
+ runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n"
+ "#include \"dir/bar.h\"\n"));
+
+ EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n",
+ runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));
+
+ EXPECT_EQ(
+ "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n",
+ runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));
+
+ EXPECT_EQ("#include <string>\nstd::string foo;\n",
+ runIncludeFixer("string foo;\n"));
+
+ // Should not match std::string.
+ EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n"));
+}
+
+TEST(IncludeFixer, IncompleteType) {
+ EXPECT_EQ(
+ "#include \"foo.h\"\n#include <string>\n"
+ "namespace std {\nclass string;\n}\nstd::string foo;\n",
+ runIncludeFixer("#include \"foo.h\"\n"
+ "namespace std {\nclass string;\n}\nstring foo;\n"));
+
+ EXPECT_EQ("#include <string>\n"
+ "class string;\ntypedef string foo;\nfoo f;\n",
+ runIncludeFixer("class string;\ntypedef string foo;\nfoo f;\n"));
+}
+
+TEST(IncludeFixer, MinimizeInclude) {
+ std::vector<std::string> IncludePath = {"-Idir/"};
+ EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
+ runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+ IncludePath = {"-isystemdir"};
+ EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n",
+ runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+ IncludePath = {"-iquotedir"};
+ EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
+ runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+ IncludePath = {"-Idir", "-Idir/otherdir"};
+ EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n",
+ runIncludeFixer("a::b::foo bar;\n", IncludePath));
+}
+
+TEST(IncludeFixer, NestedName) {
+ EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
+ "int x = a::b::foo(0);\n",
+ runIncludeFixer("int x = a::b::foo(0);\n"));
+
+ // FIXME: Handle simple macros.
+ EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n",
+ runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n"));
+ EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n",
+ runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n"));
+
+ // The empty namespace is cleaned up by clang-format after include-fixer
+ // finishes.
+ EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
+ "\nint a = a::b::foo(0);\n",
+ runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n"));
+}
+
+TEST(IncludeFixer, MultipleMissingSymbols) {
+ EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n",
+ runIncludeFixer("std::string bar;\nstd::sting foo;\n"));
+}
+
+TEST(IncludeFixer, ScopedNamespaceSymbols) {
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}",
+ runIncludeFixer("namespace a {\nb::bar b;\n}"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",
+ runIncludeFixer("namespace A {\na::b::bar b;\n}"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nvoid func() { b::bar b; }\n}",
+ runIncludeFixer("namespace a {\nvoid func() { b::bar b; }\n}"));
+ EXPECT_EQ("namespace A { c::b::bar b; }\n",
+ runIncludeFixer("namespace A { c::b::bar b; }\n"));
+ // FIXME: The header should not be added here. Remove this after we support
+ // full match.
+ EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",
+ runIncludeFixer("namespace A {\nb::bar b;\n}"));
+
+ // Finds candidates for "str::StrCat".
+ EXPECT_EQ("#include \"strcat.h\"\nnamespace foo2 {\nstr::StrCat b;\n}",
+ runIncludeFixer("namespace foo2 {\nstr::StrCat b;\n}"));
+ // str::StrCat2 doesn't exist.
+ // In these two cases, StrCat2 is a nested class of class str.
+ EXPECT_EQ("#include \"str.h\"\nnamespace foo2 {\nstr::StrCat2 b;\n}",
+ runIncludeFixer("namespace foo2 {\nstr::StrCat2 b;\n}"));
+ EXPECT_EQ("#include \"str.h\"\nnamespace ns {\nstr::StrCat2 b;\n}",
+ runIncludeFixer("namespace ns {\nstr::StrCat2 b;\n}"));
+}
+
+TEST(IncludeFixer, EnumConstantSymbols) {
+ EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
+ runIncludeFixer("int test = a::b::Green;\n"));
+}
+
+TEST(IncludeFixer, IgnoreSymbolFromHeader) {
+ std::string Code = "#include \"header.h\"";
+ EXPECT_EQ(Code, runIncludeFixer(Code));
+}
+
+// FIXME: add test cases for inserting and sorting multiple headers when
+// include-fixer supports multiple headers insertion.
+TEST(IncludeFixer, InsertAndSortSingleHeader) {
+ // Insert one header.
+ std::string Code = "#include \"a.h\"\n"
+ "#include \"foo.h\"\n"
+ "\n"
+ "namespace a {\nb::bar b;\n}\n";
+ std::string Expected = "#include \"a.h\"\n"
+ "#include \"bar.h\"\n"
+ "#include \"foo.h\"\n"
+ "\n"
+ "namespace a {\nb::bar b;\n}\n";
+ EXPECT_EQ(Expected, runIncludeFixer(Code));
+}
+
+TEST(IncludeFixer, DoNotDeleteMatchedSymbol) {
+ EXPECT_EQ("#include \"Vector.h\"\na::Vector v;",
+ runIncludeFixer("a::Vector v;"));
+}
+
+TEST(IncludeFixer, FixNamespaceQualifiers) {
+ EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+ runIncludeFixer("b::bar b;\n"));
+ EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+ runIncludeFixer("a::b::bar b;\n"));
+ EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+ runIncludeFixer("bar b;\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",
+ runIncludeFixer("namespace a {\nb::bar b;\n}\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",
+ runIncludeFixer("namespace a {\nbar b;\n}\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace b{\nbar b;\n}\n}\n",
+ runIncludeFixer("namespace a {\nnamespace b{\nbar b;\n}\n}\n"));
+ EXPECT_EQ("c::b::bar b;\n",
+ runIncludeFixer("c::b::bar b;\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar b;\n}\n",
+ runIncludeFixer("namespace d {\nbar b;\n}\n"));
+ EXPECT_EQ("#include \"bar2.h\"\nnamespace c {\na::c::bar b;\n}\n",
+ runIncludeFixer("namespace c {\nbar b;\n}\n"));
+
+ // Test common qualifers reduction.
+ EXPECT_EQ(
+ "#include \"bar.h\"\nnamespace a {\nnamespace d {\nb::bar b;\n}\n}\n",
+ runIncludeFixer("namespace a {\nnamespace d {\nbar b;\n}\n}\n"));
+ EXPECT_EQ(
+ "#include \"bar.h\"\nnamespace d {\nnamespace a {\na::b::bar b;\n}\n}\n",
+ runIncludeFixer("namespace d {\nnamespace a {\nbar b;\n}\n}\n"));
+
+ // Test nested classes.
+ EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar::t b;\n}\n",
+ runIncludeFixer("namespace d {\nbar::t b;\n}\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace c {\na::b::bar::t b;\n}\n",
+ runIncludeFixer("namespace c {\nbar::t b;\n}\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar::t b;\n}\n",
+ runIncludeFixer("namespace a {\nbar::t b;\n}\n"));
+
+ EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
+ runIncludeFixer("int test = Green;\n"));
+ EXPECT_EQ("#include \"color.h\"\nnamespace d {\nint test = a::b::Green;\n}\n",
+ runIncludeFixer("namespace d {\nint test = Green;\n}\n"));
+ EXPECT_EQ("#include \"color.h\"\nnamespace a {\nint test = b::Green;\n}\n",
+ runIncludeFixer("namespace a {\nint test = Green;\n}\n"));
+
+ // Test global scope operator.
+ EXPECT_EQ("#include \"bar.h\"\n::a::b::bar b;\n",
+ runIncludeFixer("::a::b::bar b;\n"));
+ EXPECT_EQ("#include \"bar.h\"\nnamespace a {\n::a::b::bar b;\n}\n",
+ runIncludeFixer("namespace a {\n::a::b::bar b;\n}\n"));
+}
+
+TEST(IncludeFixer, FixNamespaceQualifiersForAllInstances) {
+ const char TestCode[] = R"(
+namespace a {
+bar b;
+int func1() {
+ bar a;
+ bar *p = new bar();
+ return 0;
+}
+} // namespace a
+
+namespace a {
+bar func2() {
+ bar f;
+ return f;
+}
+} // namespace a
+
+// Non-fixed cases:
+void f() {
+ bar b;
+}
+
+namespace a {
+namespace c {
+ bar b;
+} // namespace c
+} // namespace a
+)";
+
+ const char ExpectedCode[] = R"(
+#include "bar.h"
+namespace a {
+b::bar b;
+int func1() {
+ b::bar a;
+ b::bar *p = new b::bar();
+ return 0;
+}
+} // namespace a
+
+namespace a {
+b::bar func2() {
+ b::bar f;
+ return f;
+}
+} // namespace a
+
+// Non-fixed cases:
+void f() {
+ bar b;
+}
+
+namespace a {
+namespace c {
+ bar b;
+} // namespace c
+} // namespace a
+)";
+
+ EXPECT_EQ(ExpectedCode, runIncludeFixer(TestCode));
+}
+
+TEST(IncludeFixer, DontAddQualifiersForMissingCompleteType) {
+ EXPECT_EQ("#include \"bar.h\"\nclass bar;\nvoid f() {\nbar* b;\nb->f();\n}",
+ runIncludeFixer("class bar;\nvoid f() {\nbar* b;\nb->f();\n}"));
+}
+
+} // namespace
+} // namespace include_fixer
+} // namespace clang
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include-fixer/find-all-symbols REALPATH)
+include_directories(
+ ${INCLUDE_FIXER_SOURCE_DIR}
+ )
+
+add_extra_unittest(FindAllSymbolsTests
+ FindAllSymbolsTests.cpp
+ )
+
+target_link_libraries(FindAllSymbolsTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFrontend
+ clangLex
+ clangTooling
+ findAllSymbols
+ )
--- /dev/null
+//===-- FindAllSymbolsTests.cpp - find all symbols unit tests ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "HeaderMapCollector.h"
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+
+static const char HeaderName[] = "symbols.h";
+
+class TestSymbolReporter : public SymbolReporter {
+public:
+ ~TestSymbolReporter() override {}
+
+ void reportSymbols(llvm::StringRef FileName,
+ const SymbolInfo::SignalMap &NewSymbols) override {
+ for (const auto &Entry : NewSymbols)
+ Symbols[Entry.first] += Entry.second;
+ }
+
+ int seen(const SymbolInfo &Symbol) const {
+ auto it = Symbols.find(Symbol);
+ return it == Symbols.end() ? 0 : it->second.Seen;
+ }
+
+ int used(const SymbolInfo &Symbol) const {
+ auto it = Symbols.find(Symbol);
+ return it == Symbols.end() ? 0 : it->second.Used;
+ }
+
+private:
+ SymbolInfo::SignalMap Symbols;
+};
+
+class FindAllSymbolsTest : public ::testing::Test {
+public:
+ int seen(const SymbolInfo &Symbol) { return Reporter.seen(Symbol); }
+
+ int used(const SymbolInfo &Symbol) { return Reporter.used(Symbol); }
+
+ bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) {
+ llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+ new vfs::InMemoryFileSystem);
+ llvm::IntrusiveRefCntPtr<FileManager> Files(
+ new FileManager(FileSystemOptions(), InMemoryFileSystem));
+
+ std::string FileName = "symbol.cc";
+
+ const std::string InternalHeader = "internal/internal_header.h";
+ const std::string TopHeader = "<top>";
+ // Test .inc header path. The header for `IncHeaderClass` should be
+ // internal.h, which will eventually be mapped to <top>.
+ std::string IncHeader = "internal/private.inc";
+ std::string IncHeaderCode = "class IncHeaderClass {};";
+
+ HeaderMapCollector::RegexHeaderMap RegexMap = {
+ {R"(internal_.*\.h$)", TopHeader.c_str()},
+ };
+
+ std::string InternalCode =
+ "#include \"private.inc\"\nclass Internal {};";
+ SymbolInfo InternalSymbol("Internal", SymbolInfo::SymbolKind::Class,
+ TopHeader, {});
+ SymbolInfo IncSymbol("IncHeaderClass", SymbolInfo::SymbolKind::Class,
+ TopHeader, {});
+ InMemoryFileSystem->addFile(
+ IncHeader, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode));
+ InMemoryFileSystem->addFile(InternalHeader, 0,
+ llvm::MemoryBuffer::getMemBuffer(InternalCode));
+
+ std::unique_ptr<tooling::FrontendActionFactory> Factory(
+ new FindAllSymbolsActionFactory(&Reporter, &RegexMap));
+
+ tooling::ToolInvocation Invocation(
+ {std::string("find_all_symbols"), std::string("-fsyntax-only"),
+ std::string("-std=c++11"), FileName},
+ Factory->create(), Files.get(),
+ std::make_shared<PCHContainerOperations>());
+
+ InMemoryFileSystem->addFile(HeaderName, 0,
+ llvm::MemoryBuffer::getMemBuffer(HeaderCode));
+
+ std::string Content = "#include\"" + std::string(HeaderName) +
+ "\"\n"
+ "#include \"" +
+ InternalHeader + "\"";
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+ // Test path cleaning for both decls and macros.
+ const std::string DirtyHeader = "./internal/./a/b.h";
+ Content += "\n#include \"" + DirtyHeader + "\"";
+ const std::string CleanHeader = "internal/a/b.h";
+ const std::string DirtyHeaderContent =
+ "#define INTERNAL 1\nclass ExtraInternal {};";
+ InMemoryFileSystem->addFile(
+ DirtyHeader, 0, llvm::MemoryBuffer::getMemBuffer(DirtyHeaderContent));
+ SymbolInfo DirtyMacro("INTERNAL", SymbolInfo::SymbolKind::Macro,
+ CleanHeader, {});
+ SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,
+ CleanHeader, {});
+#endif // _MSC_VER && __MINGW32__
+ Content += "\n" + MainCode.str();
+ InMemoryFileSystem->addFile(FileName, 0,
+ llvm::MemoryBuffer::getMemBuffer(Content));
+ Invocation.run();
+ EXPECT_EQ(1, seen(InternalSymbol));
+ EXPECT_EQ(1, seen(IncSymbol));
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+ EXPECT_EQ(1, seen(DirtySymbol));
+ EXPECT_EQ(1, seen(DirtyMacro));
+#endif // _MSC_VER && __MINGW32__
+ return true;
+ }
+
+protected:
+ TestSymbolReporter Reporter;
+};
+
+TEST_F(FindAllSymbolsTest, VariableSymbols) {
+ static const char Header[] = R"(
+ extern int xargc;
+ namespace na {
+ static bool SSSS = false;
+ namespace nb { const long long *XXXX; }
+ })";
+ static const char Main[] = R"(
+ auto y = &na::nb::XXXX;
+ int main() { if (na::SSSS) return xargc; }
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "nb"},
+ {SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, ExternCSymbols) {
+ static const char Header[] = R"(
+ extern "C" {
+ int C_Func() { return 0; }
+ struct C_struct {
+ int Member;
+ };
+ })";
+ static const char Main[] = R"(
+ C_struct q() {
+ int(*ptr)() = C_Func;
+ return {0};
+ }
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol =
+ SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
+ static const char Header[] = R"(
+ struct Glob {};
+ struct A; // Not a defintion, ignored.
+ class NOP; // Not a defintion, ignored
+ namespace na {
+ struct A {
+ struct AAAA {};
+ int x;
+ int y;
+ void f() {}
+ };
+ }; //
+ )";
+ static const char Main[] = R"(
+ static Glob glob;
+ static na::A::AAAA* a;
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName,
+ {{SymbolInfo::ContextType::Record, "A"},
+ {SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(0, seen(Symbol));
+ EXPECT_EQ(0, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
+ static const char Header[] = R"(
+ template <typename T>
+ struct T_TEMP {
+ template <typename _Tp1>
+ struct rebind { typedef T_TEMP<_Tp1> other; };
+ };
+ // Ignore specialization.
+ template class T_TEMP<char>;
+
+ template <typename T>
+ class Observer {
+ };
+ // Ignore specialization.
+ template <> class Observer<int> {};
+ )";
+ static const char Main[] = R"(
+ extern T_TEMP<int>::rebind<char> weirdo;
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
+ static const char Code[] = R"(
+ template<class> class Class; // undefined
+ template<class R, class... ArgTypes>
+ class Class<R(ArgTypes...)> {
+ };
+
+ template<class T> void f() {};
+ template<> void f<int>() {};
+ )";
+ runFindAllSymbols(Code, "");
+ SymbolInfo Symbol =
+ SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, FunctionSymbols) {
+ static const char Header[] = R"(
+ namespace na {
+ int gg(int);
+ int f(const int &a) { int Local; static int StaticLocal; return 0; }
+ static void SSSFFF() {}
+ } // namespace na
+ namespace na {
+ namespace nb {
+ template<typename T>
+ void fun(T t) {};
+ } // namespace nb
+ } // namespace na";
+ )";
+ static const char Main[] = R"(
+ int(*gg)(int) = &na::gg;
+ int main() {
+ (void)na::SSSFFF;
+ na::nb::fun(0);
+ return na::f(gg(0));
+ }
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "nb"},
+ {SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NamespaceTest) {
+ static const char Header[] = R"(
+ int X1;
+ namespace { int X2; }
+ namespace { namespace { int X3; } }
+ namespace { namespace nb { int X4; } }
+ namespace na { inline namespace __1 { int X5; } }
+ )";
+ static const char Main[] = R"(
+ using namespace nb;
+ int main() {
+ X1 = X2;
+ X3 = X4;
+ (void)na::X5;
+ }
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, ""}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, ""},
+ {SymbolInfo::ContextType::Namespace, ""}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "nb"},
+ {SymbolInfo::ContextType::Namespace, ""}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName,
+ {{SymbolInfo::ContextType::Namespace, "na"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
+ static const char Header[] = "void DecayedFunc(int x[], int y[10]) {}";
+ static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })";
+ runFindAllSymbols(Header, Main);
+ SymbolInfo Symbol = SymbolInfo(
+ "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CTypedefTest) {
+ static const char Header[] = R"(
+ typedef unsigned size_t_;
+ typedef struct { int x; } X;
+ using XX = X;
+ )";
+ static const char Main[] = R"(
+ size_t_ f;
+ template<typename T> struct vector{};
+ vector<X> list;
+ void foo(const XX&){}
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName,
+ HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol =
+ SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, EnumTest) {
+ static const char Header[] = R"(
+ enum Glob_E { G1, G2 };
+ enum class Altitude { high='h', low='l'};
+ enum { A1, A2 };
+ class A {
+ public:
+ enum A_ENUM { X1, X2 };
+ };
+ enum DECL : int;
+ )";
+ static const char Main[] = R"(
+ static auto flags = G1 | G2;
+ static auto alt = Altitude::high;
+ static auto nested = A::X1;
+ extern DECL whatever;
+ static auto flags2 = A1 | A2;
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(0, used(Symbol));
+
+ Symbol =
+ SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+ {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol =
+ SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+ {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol =
+ SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+ Symbol =
+ SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+ {{SymbolInfo::ContextType::EnumDecl, "Altitude"}});
+ EXPECT_EQ(0, seen(Symbol));
+ EXPECT_EQ(0, used(Symbol));
+
+ Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl,
+ HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+ Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl,
+ HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+ Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+ EXPECT_EQ(0, seen(Symbol));
+ EXPECT_EQ(0, used(Symbol));
+
+ Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
+ {{SymbolInfo::ContextType::Record, "A"}});
+ EXPECT_EQ(0, seen(Symbol));
+ EXPECT_EQ(0, used(Symbol));
+
+ Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
+ {{SymbolInfo::ContextType::EnumDecl, "A_ENUM"},
+ {SymbolInfo::ContextType::Record, "A"}});
+ EXPECT_EQ(0, seen(Symbol));
+
+ Symbol = SymbolInfo("DECL", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+ EXPECT_EQ(0, seen(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
+ static const char Header[] = R"(
+ // IWYU pragma: private, include "bar.h"
+ struct Bar {
+ };
+ )";
+ static const char Main[] = R"(
+ Bar bar;
+ )";
+ runFindAllSymbols(Header, Main);
+
+ SymbolInfo Symbol =
+ SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTest) {
+ static const char Header[] = R"(
+ #define X
+ #define Y 1
+ #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+ )";
+ static const char Main[] = R"(
+ #ifdef X
+ int main() { return MAX(0,Y); }
+ #endif
+ )";
+ runFindAllSymbols(Header, Main);
+ SymbolInfo Symbol =
+ SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
+ static const char Header[] = R"(
+ // IWYU pragma: private, include "bar.h"
+ #define X 1
+ #define Y 1
+ #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+ )";
+ static const char Main[] = R"(
+ #ifdef X
+ int main() { return MAX(0,Y); }
+ #endif
+ )";
+ runFindAllSymbols(Header, Main);
+ SymbolInfo Symbol =
+ SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+
+ Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+ EXPECT_EQ(1, seen(Symbol));
+ EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NoFriendTest) {
+ static const char Header[] = R"(
+ class WorstFriend {
+ friend void Friend();
+ friend class BestFriend;
+ };
+ )";
+ runFindAllSymbols(Header, "");
+ SymbolInfo Symbol =
+ SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(1, seen(Symbol));
+
+ Symbol =
+ SymbolInfo("Friend", SymbolInfo::SymbolKind::Function, HeaderName, {});
+ EXPECT_EQ(0, seen(Symbol));
+
+ Symbol =
+ SymbolInfo("BestFriend", SymbolInfo::SymbolKind::Class, HeaderName, {});
+ EXPECT_EQ(0, seen(Symbol));
+}
+
+} // namespace find_all_symbols
+} // namespace clang
--- /dev/null
+//===--- VirtualFileHelper.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \brief This file defines an utility class for tests that needs a source
+/// manager for a virtual file with customizable content.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
+#define CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+
+namespace clang {
+
+/// \brief Class that provides easy access to a SourceManager and that allows to
+/// map virtual files conveniently.
+class VirtualFileHelper {
+ struct VirtualFile {
+ std::string FileName;
+ std::string Code;
+ };
+
+public:
+ VirtualFileHelper()
+ : DiagOpts(new DiagnosticOptions()),
+ Diagnostics(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+ &*DiagOpts),
+ DiagnosticPrinter(llvm::outs(), &*DiagOpts),
+ Files((FileSystemOptions())) {}
+
+ /// \brief Create a virtual file \p FileName, with content \p Code.
+ void mapFile(llvm::StringRef FileName, llvm::StringRef Code) {
+ VirtualFile VF = { FileName, Code };
+ VirtualFiles.push_back(VF);
+ }
+
+ /// \brief Create a new \c SourceManager with the virtual files and contents
+ /// mapped to it.
+ SourceManager &getNewSourceManager() {
+ Sources.reset(new SourceManager(Diagnostics, Files));
+ mapVirtualFiles(*Sources);
+ return *Sources;
+ }
+
+ /// \brief Map the virtual file contents in the given \c SourceManager.
+ void mapVirtualFiles(SourceManager &SM) const {
+ for (llvm::SmallVectorImpl<VirtualFile>::const_iterator
+ I = VirtualFiles.begin(),
+ E = VirtualFiles.end();
+ I != E; ++I) {
+ std::unique_ptr<llvm::MemoryBuffer> Buf =
+ llvm::MemoryBuffer::getMemBuffer(I->Code);
+ const FileEntry *Entry = SM.getFileManager().getVirtualFile(
+ I->FileName, Buf->getBufferSize(), /*ModificationTime=*/0);
+ SM.overrideFileContents(Entry, std::move(Buf));
+ }
+ }
+
+private:
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+ DiagnosticsEngine Diagnostics;
+ TextDiagnosticPrinter DiagnosticPrinter;
+ FileManager Files;
+ // most tests don't need more than one file
+ llvm::SmallVector<VirtualFile, 1> VirtualFiles;
+ std::unique_ptr<SourceManager> Sources;
+};
+
+} // end namespace clang
+
+#endif // CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H