[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