[clang-tools-extra] r244586 - Add an IncludeInserter to clang-tidy.
Daniel Jasper via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 19 14:03:50 PDT 2015
On Tue, Aug 11, 2015 at 1:37 PM, Manuel Klimek via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
> Author: klimek
> Date: Tue Aug 11 06:37:48 2015
> New Revision: 244586
>
> URL: http://llvm.org/viewvc/llvm-project?rev=244586&view=rev
> Log:
> Add an IncludeInserter to clang-tidy.
>
> Will be used to allow checks to insert includes at the right position.
>
> Added:
> clang-tools-extra/trunk/clang-tidy/IncludeInserter.cpp
> clang-tools-extra/trunk/clang-tidy/IncludeInserter.h
> clang-tools-extra/trunk/clang-tidy/IncludeSorter.cpp
> clang-tools-extra/trunk/clang-tidy/IncludeSorter.h
> clang-tools-extra/trunk/unittests/clang-tidy/IncludeInserterTest.cpp
> Modified:
> clang-tools-extra/trunk/clang-tidy/CMakeLists.txt
> clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt
> clang-tools-extra/trunk/unittests/clang-tidy/ClangTidyTest.h
>
> Modified: clang-tools-extra/trunk/clang-tidy/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/CMakeLists.txt?rev=244586&r1=244585&r2=244586&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/clang-tidy/CMakeLists.txt Tue Aug 11 06:37:48
> 2015
> @@ -7,6 +7,8 @@ add_clang_library(clangTidy
> ClangTidyModule.cpp
> ClangTidyDiagnosticConsumer.cpp
> ClangTidyOptions.cpp
> + IncludeInserter.cpp
> + IncludeSorter.cpp
>
> DEPENDS
> ClangSACheckers
>
> Added: clang-tools-extra/trunk/clang-tidy/IncludeInserter.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/IncludeInserter.cpp?rev=244586&view=auto
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/IncludeInserter.cpp (added)
> +++ clang-tools-extra/trunk/clang-tidy/IncludeInserter.cpp Tue Aug 11
> 06:37:48 2015
> @@ -0,0 +1,77 @@
> +//===-------- 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"
> +
> +namespace clang {
> +namespace tidy {
> +
> +class IncludeInserterCallback : public PPCallbacks {
> +public:
> + explicit IncludeInserterCallback(IncludeInserter *IncludeInserter)
> + : IncludeInserter(IncludeInserter) {}
> + // 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 & /*include_token*/,
> + StringRef FileNameRef, bool IsAngled,
> + CharSourceRange FileNameRange,
> + const FileEntry * /*IncludedFile*/,
> + StringRef /*SearchPath*/, StringRef
> /*RelativePath*/,
> + const Module * /*ImportedModule*/) override {
> + IncludeInserter->AddInclude(FileNameRef, IsAngled, HashLocation,
> + FileNameRange.getEnd());
> + }
> +
> +private:
> + IncludeInserter *IncludeInserter;
> +};
> +
> +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.insert(std::make_pair(FileID,
> std::set<std::string>()))
> + .second) {
> + return llvm::None;
> + }
>
WAT? How did you come up with this??? This allows the include inserter to
insert at most one header per file.
Fixed in r245500.
> + if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
> + return llvm::None;
> + }
> + return IncludeSorterByFile[FileID]->CreateIncludeInsertion(Header,
> IsAngled);
> +}
> +
> +void IncludeInserter::AddInclude(StringRef file_name, bool IsAngled,
> + SourceLocation HashLocation,
> + SourceLocation end_location) {
> + 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(file_name, IsAngled,
> HashLocation,
> + end_location);
> +}
> +
> +} // namespace tidy
> +} // namespace clang
>
> Added: clang-tools-extra/trunk/clang-tidy/IncludeInserter.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/IncludeInserter.h?rev=244586&view=auto
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/IncludeInserter.h (added)
> +++ clang-tools-extra/trunk/clang-tidy/IncludeInserter.h Tue Aug 11
> 06:37:48 2015
> @@ -0,0 +1,75 @@
> +//===---------- 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 {
> +
> +// IncludeInserter can be used by ClangTidyChecks in the following
> fashion:
> +// 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;
> +// };
> +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 Header inclusion directive fixit. Returns None on error or
> + // if inclusion directive already exists.
> + llvm::Optional<FixItHint>
> + CreateIncludeInsertion(FileID FileID, llvm::StringRef Header, bool
> IsAngled);
> +
> +private:
> + void AddInclude(StringRef file_name, bool IsAngled,
> + SourceLocation hash_location, SourceLocation
> end_location);
> +
> + 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 tidy
> +} // namespace clang
> +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
>
> Added: clang-tools-extra/trunk/clang-tidy/IncludeSorter.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/IncludeSorter.cpp?rev=244586&view=auto
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/IncludeSorter.cpp (added)
> +++ clang-tools-extra/trunk/clang-tidy/IncludeSorter.cpp Tue Aug 11
> 06:37:48 2015
> @@ -0,0 +1,267 @@
> +//===---------- 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 {
> +
> +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.equals(CanonicalInclude)) {
> + 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) {
> + if (SourceLocations.empty()) {
> + return None;
> + }
> + std::string IncludeStmt =
> + IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
> + : llvm::Twine("#include \"" + FileName + "\"\n").str();
> + 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);
> + }
> + IncludeKinds NonEmptyKind = IK_InvalidInclude;
> + for (int i = IncludeKind - 1; i >= 0; --i) {
> + if (!IncludeBucket[i].empty()) {
> + NonEmptyKind = static_cast<IncludeKinds>(i);
> + break;
> + }
> + }
> + if (NonEmptyKind == IK_InvalidInclude) {
> + return llvm::None;
> + }
> + const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
> + SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
> + IncludeStmt.append("\n");
> + return FixItHint::CreateInsertion(
> + LastIncludeLocation.getEnd().getLocWithOffset(1), 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(),
> + [](const std::string &Left, const std::string &Right) {
> + return llvm::StringRef(Left).compare_lower(Right) < 0;
> + });
> + 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) {
> + const SourceRange &EditRange = LineEdit.second.first;
> + // 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()) {
> + 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(CreateFixIt(CurrentRange, CurrentText));
> + }
> +
> + CurrentEndLine = LineEdit.first;
> + CurrentRange = EditRange;
> + CurrentText = LineEdit.second.second;
> + }
> + }
> + // Finally, report the current block edit if there is one.
> + if (CurrentEndLine) {
> + Fixes.push_back(CreateFixIt(CurrentRange, CurrentText));
> + }
> +
> + // Reset the remaining internal state.
> + SourceLocations.clear();
> + IncludeLocations.clear();
> + return Fixes;
> +}
> +
> +// Creates a fix-it for the given replacements.
> +// Takes the the source location that will be replaced, and the new text.
> +FixItHint IncludeSorter::CreateFixIt(SourceRange EditRange,
> + const std::string &NewText) {
> + FixItHint Fix;
> + Fix.RemoveRange = CharSourceRange::getCharRange(EditRange);
> + Fix.CodeToInsert = NewText;
> + return Fix;
> +}
> +
> +} // namespace tidy
> +} // namespace clang
>
> Added: clang-tools-extra/trunk/clang-tidy/IncludeSorter.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/IncludeSorter.h?rev=244586&view=auto
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-tidy/IncludeSorter.h (added)
> +++ clang-tools-extra/trunk/clang-tidy/IncludeSorter.h Tue Aug 11 06:37:48
> 2015
> @@ -0,0 +1,87 @@
> +//===------------ 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 {
> +
> +// Class used by IncludeSorterCallback and 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
> +// enocded in IncludeKinds.
> +class IncludeSorter {
> + public:
> + // Supported include styles.
> + enum IncludeStyle {
> + IS_LLVM = 0,
> + IS_Google = 1
> + };
> +
> + // 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 IncludeKinds
> + };
> +
> + // 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 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;
> +
> + // Creates a fix-it for the given replacements.
> + // Takes the the source location that will be replaced, and the new
> text.
> + FixItHint CreateFixIt(SourceRange EditRange, const std::string&
> NewText);
> +
> + 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 tidy
> +} // namespace clang
> +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
>
> Modified: clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt?rev=244586&r1=244585&r2=244586&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/unittests/clang-tidy/CMakeLists.txt Tue Aug 11
> 06:37:48 2015
> @@ -9,6 +9,7 @@ include_directories(${CLANG_LINT_SOURCE_
> add_extra_unittest(ClangTidyTests
> ClangTidyDiagnosticConsumerTest.cpp
> ClangTidyOptionsTest.cpp
> + IncludeInserterTest.cpp
> GoogleModuleTest.cpp
> LLVMModuleTest.cpp
> MiscModuleTest.cpp
>
> Modified: clang-tools-extra/trunk/unittests/clang-tidy/ClangTidyTest.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/ClangTidyTest.h?rev=244586&r1=244585&r2=244586&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clang-tidy/ClangTidyTest.h (original)
> +++ clang-tools-extra/trunk/unittests/clang-tidy/ClangTidyTest.h Tue Aug
> 11 06:37:48 2015
> @@ -17,6 +17,7 @@
> #include "clang/Frontend/FrontendActions.h"
> #include "clang/Tooling/Refactoring.h"
> #include "clang/Tooling/Tooling.h"
> +#include <map>
>
> namespace clang {
> namespace tidy {
> @@ -46,7 +47,8 @@ std::string
> runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors =
> nullptr,
> const Twine &Filename = "input.cc",
> ArrayRef<std::string> ExtraArgs = None,
> - const ClangTidyOptions &ExtraOptions = ClangTidyOptions())
> {
> + const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),
> + std::map<StringRef, StringRef> PathsToContent = {}) {
> ClangTidyOptions Options = ExtraOptions;
> Options.Checks = "*";
> ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
> @@ -60,6 +62,7 @@ runCheckOnCode(StringRef Code, std::vect
> 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());
> llvm::IntrusiveRefCntPtr<FileManager> Files(
> @@ -67,6 +70,10 @@ runCheckOnCode(StringRef Code, std::vect
> tooling::ToolInvocation Invocation(
> ArgCXX11, new TestClangTidyAction(Check, Finder, Context),
> Files.get());
> Invocation.mapVirtualFile(Filename.str(), Code);
> + for (const auto & FileContent : PathsToContent) {
> + Invocation.mapVirtualFile(Twine("include/" + FileContent.first).str(),
> + FileContent.second);
> + }
> Invocation.setDiagnosticConsumer(&DiagConsumer);
> if (!Invocation.run())
> return "";
>
> Added: clang-tools-extra/trunk/unittests/clang-tidy/IncludeInserterTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-tidy/IncludeInserterTest.cpp?rev=244586&view=auto
>
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clang-tidy/IncludeInserterTest.cpp
> (added)
> +++ clang-tools-extra/trunk/unittests/clang-tidy/IncludeInserterTest.cpp
> Tue Aug 11 06:37:48 2015
> @@ -0,0 +1,407 @@
> +//===---- 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/IncludeInserter.h"
> +#include "clang/Lex/Preprocessor.h"
> +#include "clang/Frontend/CompilerInstance.h"
> +#include "ClangTidyTest.h"
> +#include "gtest/gtest.h"
> +
> +namespace clang {
> +namespace tidy {
> +namespace {
> +
> +class IncludeInserterCheckBase : public ClangTidyCheck {
> +public:
> + using ClangTidyCheck::ClangTidyCheck;
> + void registerPPCallbacks(CompilerInstance &Compiler) override {
> + Inserter.reset(new IncludeInserter(Compiler.getSourceManager(),
> + Compiler.getLangOpts(),
> + 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 Fixit =
> +
> Inserter->CreateIncludeInsertion(Result.SourceManager->getMainFileID(),
> + HeaderToInclude(),
> IsAngledInclude());
> + if (Fixit) {
> + diag(Result.Nodes.getStmtAs<DeclStmt>("stmt")->getLocStart(), "foo,
> bar")
> + << *Fixit;
> + }
> + // Second include should yield no Fixit.
> + Fixit =
> +
> Inserter->CreateIncludeInsertion(Result.SourceManager->getMainFileID(),
> + HeaderToInclude(),
> IsAngledInclude());
> + EXPECT_FALSE(Fixit);
> + }
> +
> + virtual StringRef HeaderToInclude() const = 0;
> + virtual bool IsAngledInclude() const = 0;
> +
> + std::unique_ptr<IncludeInserter> Inserter;
> +};
> +
> +class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
> +public:
> + using IncludeInserterCheckBase::IncludeInserterCheckBase;
> + StringRef HeaderToInclude() const override { return "path/to/header.h";
> }
> + bool IsAngledInclude() const override { return false; }
> +};
> +
> +class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {
> +public:
> + using IncludeInserterCheckBase::IncludeInserterCheckBase;
> + StringRef HeaderToInclude() const override { return "set"; }
> + bool IsAngledInclude() const override { return true; }
> +};
> +
> +template <typename Check>
> +std::string runCheckOnCode(StringRef Code, StringRef Filename,
> + size_t NumWarningsExpected) {
> + std::vector<ClangTidyError> Errors;
> + return test::runCheckOnCode<Check>(Code, &Errors, Filename, None,
> + ClangTidyOptions(),
> + {// Main file include
> + {"devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.h",
> + "\n"},
> + // Non system headers
> + {"path/to/a/header.h", "\n"},
> + {"path/to/z/header.h", "\n"},
> + {"path/to/header.h", "\n"},
> + // Fake system headers.
> + {"stdlib.h", "\n"},
> + {"unistd.h", "\n"},
> + {"list", "\n"},
> + {"map", "\n"},
> + {"set", "\n"},
> + {"vector", "\n"}});
> + EXPECT_EQ(NumWarningsExpected, Errors.size());
>
I am curious, how often has this fired during development? Being after the
return and all ...
> +}
> +
> +TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_input2.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_input2.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_input2.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_input2.cc",
> + 0));
> +}
> +
> +TEST(IncludeInserterTest,
> InsertNonSystemIncludeAfterLastCXXSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
> +
> +#include <list>
> +#include <map>
> +
> +void foo() {
> + int a = 0;
> +})";
> + const char *PostCode = R"(
> +#include "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
> +
> +void foo() {
> + int a = 0;
> +})";
> + const char *PostCode = R"(
> +#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
> +
> +#include "path/to/header.h"
> +
> +void foo() {
> + int a = 0;
> +})";
> +
> + EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
> + PreCode, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest,
> InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest,
> InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes)
> {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
> +
> +#include "path/to/a/header.h"
> +
> +void foo() {
> + int a = 0;
> +})";
> + const char *PostCode = R"(
> +#include "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
> + const char *PreCode = R"(
> +#include "devtools/cymbal/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 "devtools/cymbal/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, "devtools/cymbal/clang_tidy/tests/"
> + "insert_includes_test_header.cc",
> + 1));
> +}
> +
> +} // namespace
> +} // namespace tidy
> +} // namespace clang
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20150819/6504beeb/attachment-0001.html>
More information about the cfe-commits
mailing list