On Tue, Aug 11, 2015 at 1:37 PM, Manuel Klimek via cfe-commits < cfe-commits@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@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits