Index: cfe/trunk/include/clang/Format/Format.h =================================================================== --- cfe/trunk/include/clang/Format/Format.h +++ cfe/trunk/include/clang/Format/Format.h @@ -16,8 +16,8 @@ #define LLVM_CLANG_FORMAT_FORMAT_H #include "clang/Basic/LangOptions.h" -#include "clang/Tooling/Core/IncludeStyle.h" #include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Inclusions/IncludeStyle.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Regex.h" #include Index: cfe/trunk/include/clang/Tooling/Core/HeaderIncludes.h =================================================================== --- cfe/trunk/include/clang/Tooling/Core/HeaderIncludes.h +++ cfe/trunk/include/clang/Tooling/Core/HeaderIncludes.h @@ -1,137 +0,0 @@ -//===--- HeaderIncludes.h - Insert/Delete #includes for C++ code--*- C++-*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_CORE_HEADERINCLUDES_H -#define LLVM_CLANG_TOOLING_CORE_HEADERINCLUDES_H - -#include "clang/Basic/SourceManager.h" -#include "clang/Tooling/Core/Replacement.h" -#include "clang/Tooling/Core/IncludeStyle.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/Regex.h" -#include - -namespace clang { -namespace tooling { - -/// This class manages priorities of C++ #include categories and calculates -/// priorities for headers. -/// FIXME(ioeric): move this class into implementation file when clang-format's -/// include sorting functions are also moved here. -class IncludeCategoryManager { -public: - IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName); - - /// Returns the priority of the category which \p IncludeName belongs to. - /// If \p CheckMainHeader is true and \p IncludeName is a main header, returns - /// 0. Otherwise, returns the priority of the matching category or INT_MAX. - /// NOTE: this API is not thread-safe! - int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; - -private: - bool isMainHeader(StringRef IncludeName) const; - - const IncludeStyle Style; - bool IsMainFile; - std::string FileName; - // This refers to a substring in FileName. - StringRef FileStem; - // Regex is not thread-safe. - mutable SmallVector CategoryRegexs; -}; - -/// Generates replacements for inserting or deleting #include directives in a -/// file. -class HeaderIncludes { -public: - HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, - const IncludeStyle &Style); - - /// Inserts an #include directive of \p Header into the code. If \p IsAngled - /// is true, \p Header will be quoted with <> in the directive; otherwise, it - /// will be quoted with "". - /// - /// When searching for points to insert new header, this ignores #include's - /// after the #include block(s) in the beginning of a file to avoid inserting - /// headers into code sections where new #include's should not be added by - /// default. These code sections include: - /// - raw string literals (containing #include). - /// - #if blocks. - /// - Special #include's among declarations (e.g. functions). - /// - /// Returns a replacement that inserts the new header into a suitable #include - /// block of the same category. This respects the order of the existing - /// #includes in the block; if the existing #includes are not already sorted, - /// this will simply insert the #include in front of the first #include of the - /// same category in the code that should be sorted after \p IncludeName. If - /// \p IncludeName already exists (with exactly the same spelling), this - /// returns None. - llvm::Optional insert(llvm::StringRef Header, - bool IsAngled) const; - - /// Removes all existing #includes of \p Header quoted with <> if \p IsAngled - /// is true or "" if \p IsAngled is false. - /// This doesn't resolve the header file path; it only deletes #includes with - /// exactly the same spelling. - tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const; - -private: - struct Include { - Include(StringRef Name, tooling::Range R) : Name(Name), R(R) {} - - // An include header quoted with either <> or "". - std::string Name; - // The range of the whole line of include directive including any eading - // whitespaces and trailing comment. - tooling::Range R; - }; - - void addExistingInclude(Include IncludeToAdd, unsigned NextLineOffset); - - std::string FileName; - std::string Code; - - // Map from include name (quotation trimmed) to a list of existing includes - // (in case there are more than one) with the name in the current file. - // and "x" will be treated as the same header when deleting #includes. - llvm::StringMap> ExistingIncludes; - - /// Map from priorities of #include categories to all #includes in the same - /// category. This is used to find #includes of the same category when - /// inserting new #includes. #includes in the same categories are sorted in - /// in the order they appear in the source file. - /// See comment for "FormatStyle::IncludeCategories" for details about include - /// priorities. - std::unordered_map> - IncludesByPriority; - - int FirstIncludeOffset; - // All new headers should be inserted after this offset (e.g. after header - // guards, file comment). - unsigned MinInsertOffset; - // Max insertion offset in the original code. For example, we want to avoid - // inserting new #includes into the actual code section (e.g. after a - // declaration). - unsigned MaxInsertOffset; - IncludeCategoryManager Categories; - // Record the offset of the end of the last include in each category. - std::unordered_map CategoryEndOffsets; - - // All possible priorities. - std::set Priorities; - - // Matches a whole #include directive. - llvm::Regex IncludeRegex; -}; - - -} // namespace tooling -} // namespace clang - -#endif // LLVM_CLANG_TOOLING_CORE_HEADERINCLUDES_H Index: cfe/trunk/include/clang/Tooling/Core/IncludeStyle.h =================================================================== --- cfe/trunk/include/clang/Tooling/Core/IncludeStyle.h +++ cfe/trunk/include/clang/Tooling/Core/IncludeStyle.h @@ -1,133 +0,0 @@ -//===--- IncludeStyle.h - Style of C++ #include directives -------*- C++-*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLING_CORE_INCLUDESTYLE_H -#define LLVM_CLANG_TOOLING_CORE_INCLUDESTYLE_H - -#include "llvm/Support/YAMLTraits.h" -#include -#include - -namespace clang { -namespace tooling { - -/// Style for sorting and grouping C++ #include directives. -struct IncludeStyle { - /// Styles for sorting multiple ``#include`` blocks. - enum IncludeBlocksStyle { - /// Sort each ``#include`` block separately. - /// \code - /// #include "b.h" into #include "b.h" - /// - /// #include #include "a.h" - /// #include "a.h" #include - /// \endcode - IBS_Preserve, - /// Merge multiple ``#include`` blocks together and sort as one. - /// \code - /// #include "b.h" into #include "a.h" - /// #include "b.h" - /// #include #include - /// #include "a.h" - /// \endcode - IBS_Merge, - /// Merge multiple ``#include`` blocks together and sort as one. - /// Then split into groups based on category priority. See - /// ``IncludeCategories``. - /// \code - /// #include "b.h" into #include "a.h" - /// #include "b.h" - /// #include - /// #include "a.h" #include - /// \endcode - IBS_Regroup, - }; - - /// Dependent on the value, multiple ``#include`` blocks can be sorted - /// as one and divided based on category. - IncludeBlocksStyle IncludeBlocks; - - /// See documentation of ``IncludeCategories``. - struct IncludeCategory { - /// The regular expression that this category matches. - std::string Regex; - /// The priority to assign to this category. - int Priority; - bool operator==(const IncludeCategory &Other) const { - return Regex == Other.Regex && Priority == Other.Priority; - } - }; - - /// Regular expressions denoting the different ``#include`` categories - /// used for ordering ``#includes``. - /// - /// These regular expressions are matched against the filename of an include - /// (including the <> or "") in order. The value belonging to the first - /// matching regular expression is assigned and ``#includes`` are sorted first - /// according to increasing category number and then alphabetically within - /// each category. - /// - /// If none of the regular expressions match, INT_MAX is assigned as - /// category. The main header for a source file automatically gets category 0. - /// so that it is generally kept at the beginning of the ``#includes`` - /// (http://llvm.org/docs/CodingStandards.html#include-style). However, you - /// can also assign negative priorities if you have certain headers that - /// always need to be first. - /// - /// To configure this in the .clang-format file, use: - /// \code{.yaml} - /// IncludeCategories: - /// - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - /// Priority: 2 - /// - Regex: '^(<|"(gtest|gmock|isl|json)/)' - /// Priority: 3 - /// - Regex: '.*' - /// Priority: 1 - /// \endcode - std::vector IncludeCategories; - - /// Specify a regular expression of suffixes that are allowed in the - /// file-to-main-include mapping. - /// - /// When guessing whether a #include is the "main" include (to assign - /// category 0, see above), use this regex of allowed suffixes to the header - /// stem. A partial match is done, so that: - /// - "" means "arbitrary suffix" - /// - "$" means "no suffix" - /// - /// For example, if configured to "(_test)?$", then a header a.h would be seen - /// as the "main" include in both a.cc and a_test.cc. - std::string IncludeIsMainRegex; -}; - -} // namespace tooling -} // namespace clang - -LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::IncludeStyle::IncludeCategory) - -namespace llvm { -namespace yaml { - -template <> -struct MappingTraits { - static void mapping(IO &IO, - clang::tooling::IncludeStyle::IncludeCategory &Category); -}; - -template <> -struct ScalarEnumerationTraits< - clang::tooling::IncludeStyle::IncludeBlocksStyle> { - static void - enumeration(IO &IO, clang::tooling::IncludeStyle::IncludeBlocksStyle &Value); -}; - -} // namespace yaml -} // namespace llvm - -#endif // LLVM_CLANG_TOOLING_CORE_INCLUDESTYLE_H Index: cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h =================================================================== --- cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ cfe/trunk/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -0,0 +1,137 @@ +//===--- HeaderIncludes.h - Insert/Delete #includes for C++ code--*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H +#define LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H + +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Inclusions/IncludeStyle.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" +#include + +namespace clang { +namespace tooling { + +/// This class manages priorities of C++ #include categories and calculates +/// priorities for headers. +/// FIXME(ioeric): move this class into implementation file when clang-format's +/// include sorting functions are also moved here. +class IncludeCategoryManager { +public: + IncludeCategoryManager(const IncludeStyle &Style, StringRef FileName); + + /// Returns the priority of the category which \p IncludeName belongs to. + /// If \p CheckMainHeader is true and \p IncludeName is a main header, returns + /// 0. Otherwise, returns the priority of the matching category or INT_MAX. + /// NOTE: this API is not thread-safe! + int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; + +private: + bool isMainHeader(StringRef IncludeName) const; + + const IncludeStyle Style; + bool IsMainFile; + std::string FileName; + // This refers to a substring in FileName. + StringRef FileStem; + // Regex is not thread-safe. + mutable SmallVector CategoryRegexs; +}; + +/// Generates replacements for inserting or deleting #include directives in a +/// file. +class HeaderIncludes { +public: + HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, + const IncludeStyle &Style); + + /// Inserts an #include directive of \p Header into the code. If \p IsAngled + /// is true, \p Header will be quoted with <> in the directive; otherwise, it + /// will be quoted with "". + /// + /// When searching for points to insert new header, this ignores #include's + /// after the #include block(s) in the beginning of a file to avoid inserting + /// headers into code sections where new #include's should not be added by + /// default. These code sections include: + /// - raw string literals (containing #include). + /// - #if blocks. + /// - Special #include's among declarations (e.g. functions). + /// + /// Returns a replacement that inserts the new header into a suitable #include + /// block of the same category. This respects the order of the existing + /// #includes in the block; if the existing #includes are not already sorted, + /// this will simply insert the #include in front of the first #include of the + /// same category in the code that should be sorted after \p IncludeName. If + /// \p IncludeName already exists (with exactly the same spelling), this + /// returns None. + llvm::Optional insert(llvm::StringRef Header, + bool IsAngled) const; + + /// Removes all existing #includes of \p Header quoted with <> if \p IsAngled + /// is true or "" if \p IsAngled is false. + /// This doesn't resolve the header file path; it only deletes #includes with + /// exactly the same spelling. + tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const; + +private: + struct Include { + Include(StringRef Name, tooling::Range R) : Name(Name), R(R) {} + + // An include header quoted with either <> or "". + std::string Name; + // The range of the whole line of include directive including any eading + // whitespaces and trailing comment. + tooling::Range R; + }; + + void addExistingInclude(Include IncludeToAdd, unsigned NextLineOffset); + + std::string FileName; + std::string Code; + + // Map from include name (quotation trimmed) to a list of existing includes + // (in case there are more than one) with the name in the current file. + // and "x" will be treated as the same header when deleting #includes. + llvm::StringMap> ExistingIncludes; + + /// Map from priorities of #include categories to all #includes in the same + /// category. This is used to find #includes of the same category when + /// inserting new #includes. #includes in the same categories are sorted in + /// in the order they appear in the source file. + /// See comment for "FormatStyle::IncludeCategories" for details about include + /// priorities. + std::unordered_map> + IncludesByPriority; + + int FirstIncludeOffset; + // All new headers should be inserted after this offset (e.g. after header + // guards, file comment). + unsigned MinInsertOffset; + // Max insertion offset in the original code. For example, we want to avoid + // inserting new #includes into the actual code section (e.g. after a + // declaration). + unsigned MaxInsertOffset; + IncludeCategoryManager Categories; + // Record the offset of the end of the last include in each category. + std::unordered_map CategoryEndOffsets; + + // All possible priorities. + std::set Priorities; + + // Matches a whole #include directive. + llvm::Regex IncludeRegex; +}; + + +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_INCLUSIONS_HEADERINCLUDES_H Index: cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h =================================================================== --- cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h +++ cfe/trunk/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -0,0 +1,133 @@ +//===--- IncludeStyle.h - Style of C++ #include directives -------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_INCLUSIONS_INCLUDESTYLE_H +#define LLVM_CLANG_TOOLING_INCLUSIONS_INCLUDESTYLE_H + +#include "llvm/Support/YAMLTraits.h" +#include +#include + +namespace clang { +namespace tooling { + +/// Style for sorting and grouping C++ #include directives. +struct IncludeStyle { + /// Styles for sorting multiple ``#include`` blocks. + enum IncludeBlocksStyle { + /// Sort each ``#include`` block separately. + /// \code + /// #include "b.h" into #include "b.h" + /// + /// #include #include "a.h" + /// #include "a.h" #include + /// \endcode + IBS_Preserve, + /// Merge multiple ``#include`` blocks together and sort as one. + /// \code + /// #include "b.h" into #include "a.h" + /// #include "b.h" + /// #include #include + /// #include "a.h" + /// \endcode + IBS_Merge, + /// Merge multiple ``#include`` blocks together and sort as one. + /// Then split into groups based on category priority. See + /// ``IncludeCategories``. + /// \code + /// #include "b.h" into #include "a.h" + /// #include "b.h" + /// #include + /// #include "a.h" #include + /// \endcode + IBS_Regroup, + }; + + /// Dependent on the value, multiple ``#include`` blocks can be sorted + /// as one and divided based on category. + IncludeBlocksStyle IncludeBlocks; + + /// See documentation of ``IncludeCategories``. + struct IncludeCategory { + /// The regular expression that this category matches. + std::string Regex; + /// The priority to assign to this category. + int Priority; + bool operator==(const IncludeCategory &Other) const { + return Regex == Other.Regex && Priority == Other.Priority; + } + }; + + /// Regular expressions denoting the different ``#include`` categories + /// used for ordering ``#includes``. + /// + /// These regular expressions are matched against the filename of an include + /// (including the <> or "") in order. The value belonging to the first + /// matching regular expression is assigned and ``#includes`` are sorted first + /// according to increasing category number and then alphabetically within + /// each category. + /// + /// If none of the regular expressions match, INT_MAX is assigned as + /// category. The main header for a source file automatically gets category 0. + /// so that it is generally kept at the beginning of the ``#includes`` + /// (http://llvm.org/docs/CodingStandards.html#include-style). However, you + /// can also assign negative priorities if you have certain headers that + /// always need to be first. + /// + /// To configure this in the .clang-format file, use: + /// \code{.yaml} + /// IncludeCategories: + /// - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + /// Priority: 2 + /// - Regex: '^(<|"(gtest|gmock|isl|json)/)' + /// Priority: 3 + /// - Regex: '.*' + /// Priority: 1 + /// \endcode + std::vector IncludeCategories; + + /// Specify a regular expression of suffixes that are allowed in the + /// file-to-main-include mapping. + /// + /// When guessing whether a #include is the "main" include (to assign + /// category 0, see above), use this regex of allowed suffixes to the header + /// stem. A partial match is done, so that: + /// - "" means "arbitrary suffix" + /// - "$" means "no suffix" + /// + /// For example, if configured to "(_test)?$", then a header a.h would be seen + /// as the "main" include in both a.cc and a_test.cc. + std::string IncludeIsMainRegex; +}; + +} // namespace tooling +} // namespace clang + +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::IncludeStyle::IncludeCategory) + +namespace llvm { +namespace yaml { + +template <> +struct MappingTraits { + static void mapping(IO &IO, + clang::tooling::IncludeStyle::IncludeCategory &Category); +}; + +template <> +struct ScalarEnumerationTraits< + clang::tooling::IncludeStyle::IncludeBlocksStyle> { + static void + enumeration(IO &IO, clang::tooling::IncludeStyle::IncludeBlocksStyle &Value); +}; + +} // namespace yaml +} // namespace llvm + +#endif // LLVM_CLANG_TOOLING_INCLUSIONS_INCLUDESTYLE_H Index: cfe/trunk/include/clang/module.modulemap =================================================================== --- cfe/trunk/include/clang/module.modulemap +++ cfe/trunk/include/clang/module.modulemap @@ -153,3 +153,8 @@ requires cplusplus umbrella "Tooling/Core" module * { export * } } + +module Clang_ToolingInclusions { + requires cplusplus + umbrella "Tooling/Inclusions" module * { export * } +} Index: cfe/trunk/lib/Format/CMakeLists.txt =================================================================== --- cfe/trunk/lib/Format/CMakeLists.txt +++ cfe/trunk/lib/Format/CMakeLists.txt @@ -20,4 +20,5 @@ clangBasic clangLex clangToolingCore + clangToolingInclusions ) Index: cfe/trunk/lib/Format/Format.cpp =================================================================== --- cfe/trunk/lib/Format/Format.cpp +++ cfe/trunk/lib/Format/Format.cpp @@ -31,7 +31,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/VirtualFileSystem.h" #include "clang/Lex/Lexer.h" -#include "clang/Tooling/Core/HeaderIncludes.h" +#include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" Index: cfe/trunk/lib/Tooling/CMakeLists.txt =================================================================== --- cfe/trunk/lib/Tooling/CMakeLists.txt +++ cfe/trunk/lib/Tooling/CMakeLists.txt @@ -4,6 +4,7 @@ ) add_subdirectory(Core) +add_subdirectory(Inclusions) add_subdirectory(Refactoring) add_subdirectory(ASTDiff) Index: cfe/trunk/lib/Tooling/Core/CMakeLists.txt =================================================================== --- cfe/trunk/lib/Tooling/Core/CMakeLists.txt +++ cfe/trunk/lib/Tooling/Core/CMakeLists.txt @@ -2,8 +2,6 @@ add_clang_library(clangToolingCore Diagnostic.cpp - HeaderIncludes.cpp - IncludeStyle.cpp Lookup.cpp Replacement.cpp Index: cfe/trunk/lib/Tooling/Core/HeaderIncludes.cpp =================================================================== --- cfe/trunk/lib/Tooling/Core/HeaderIncludes.cpp +++ cfe/trunk/lib/Tooling/Core/HeaderIncludes.cpp @@ -1,330 +0,0 @@ -//===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/Core/HeaderIncludes.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Lex/Lexer.h" - -namespace clang { -namespace tooling { -namespace { - -LangOptions createLangOpts() { - LangOptions LangOpts; - LangOpts.CPlusPlus = 1; - LangOpts.CPlusPlus11 = 1; - LangOpts.CPlusPlus14 = 1; - LangOpts.LineComment = 1; - LangOpts.CXXOperatorNames = 1; - LangOpts.Bool = 1; - LangOpts.ObjC1 = 1; - LangOpts.ObjC2 = 1; - LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally. - LangOpts.DeclSpecKeyword = 1; // To get __declspec. - LangOpts.WChar = 1; // To get wchar_t - return LangOpts; -} - -// Returns the offset after skipping a sequence of tokens, matched by \p -// GetOffsetAfterSequence, from the start of the code. -// \p GetOffsetAfterSequence should be a function that matches a sequence of -// tokens and returns an offset after the sequence. -unsigned getOffsetAfterTokenSequence( - StringRef FileName, StringRef Code, const IncludeStyle &Style, - llvm::function_ref - GetOffsetAfterSequence) { - SourceManagerForFile VirtualSM(FileName, Code); - SourceManager &SM = VirtualSM.get(); - Lexer Lex(SM.getMainFileID(), SM.getBuffer(SM.getMainFileID()), SM, - createLangOpts()); - Token Tok; - // Get the first token. - Lex.LexFromRawLexer(Tok); - return GetOffsetAfterSequence(SM, Lex, Tok); -} - -// Check if a sequence of tokens is like "# ". If it is, -// \p Tok will be the token after this directive; otherwise, it can be any token -// after the given \p Tok (including \p Tok). -bool checkAndConsumeDirectiveWithName(Lexer &Lex, StringRef Name, Token &Tok) { - bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && - Tok.is(tok::raw_identifier) && - Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) && - Tok.is(tok::raw_identifier); - if (Matched) - Lex.LexFromRawLexer(Tok); - return Matched; -} - -void skipComments(Lexer &Lex, Token &Tok) { - while (Tok.is(tok::comment)) - if (Lex.LexFromRawLexer(Tok)) - return; -} - -// Returns the offset after header guard directives and any comments -// before/after header guards. If no header guard presents in the code, this -// will returns the offset after skipping all comments from the start of the -// code. -unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName, - StringRef Code, - const IncludeStyle &Style) { - return getOffsetAfterTokenSequence( - FileName, Code, Style, - [](const SourceManager &SM, Lexer &Lex, Token Tok) { - skipComments(Lex, Tok); - unsigned InitialOffset = SM.getFileOffset(Tok.getLocation()); - if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) { - skipComments(Lex, Tok); - if (checkAndConsumeDirectiveWithName(Lex, "define", Tok)) - return SM.getFileOffset(Tok.getLocation()); - } - return InitialOffset; - }); -} - -// Check if a sequence of tokens is like -// "#include ("header.h" | )". -// If it is, \p Tok will be the token after this directive; otherwise, it can be -// any token after the given \p Tok (including \p Tok). -bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) { - auto Matched = [&]() { - Lex.LexFromRawLexer(Tok); - return true; - }; - if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && - Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "include") { - if (Lex.LexFromRawLexer(Tok)) - return false; - if (Tok.is(tok::string_literal)) - return Matched(); - if (Tok.is(tok::less)) { - while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) { - } - if (Tok.is(tok::greater)) - return Matched(); - } - } - return false; -} - -// Returns the offset of the last #include directive after which a new -// #include can be inserted. This ignores #include's after the #include block(s) -// in the beginning of a file to avoid inserting headers into code sections -// where new #include's should not be added by default. -// These code sections include: -// - raw string literals (containing #include). -// - #if blocks. -// - Special #include's among declarations (e.g. functions). -// -// If no #include after which a new #include can be inserted, this returns the -// offset after skipping all comments from the start of the code. -// Inserting after an #include is not allowed if it comes after code that is not -// #include (e.g. pre-processing directive that is not #include, declarations). -unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code, - const IncludeStyle &Style) { - return getOffsetAfterTokenSequence( - FileName, Code, Style, - [](const SourceManager &SM, Lexer &Lex, Token Tok) { - skipComments(Lex, Tok); - unsigned MaxOffset = SM.getFileOffset(Tok.getLocation()); - while (checkAndConsumeInclusiveDirective(Lex, Tok)) - MaxOffset = SM.getFileOffset(Tok.getLocation()); - return MaxOffset; - }); -} - -inline StringRef trimInclude(StringRef IncludeName) { - return IncludeName.trim("\"<>"); -} - -const char IncludeRegexPattern[] = - R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; - -} // anonymous namespace - -IncludeCategoryManager::IncludeCategoryManager(const IncludeStyle &Style, - StringRef FileName) - : Style(Style), FileName(FileName) { - FileStem = llvm::sys::path::stem(FileName); - for (const auto &Category : Style.IncludeCategories) - CategoryRegexs.emplace_back(Category.Regex, llvm::Regex::IgnoreCase); - IsMainFile = FileName.endswith(".c") || FileName.endswith(".cc") || - FileName.endswith(".cpp") || FileName.endswith(".c++") || - FileName.endswith(".cxx") || FileName.endswith(".m") || - FileName.endswith(".mm"); -} - -int IncludeCategoryManager::getIncludePriority(StringRef IncludeName, - bool CheckMainHeader) const { - int Ret = INT_MAX; - for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) - if (CategoryRegexs[i].match(IncludeName)) { - Ret = Style.IncludeCategories[i].Priority; - break; - } - if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) - Ret = 0; - return Ret; -} - -bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { - if (!IncludeName.startswith("\"")) - return false; - StringRef HeaderStem = - llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1)); - if (FileStem.startswith(HeaderStem) || - FileStem.startswith_lower(HeaderStem)) { - llvm::Regex MainIncludeRegex((HeaderStem + Style.IncludeIsMainRegex).str(), - llvm::Regex::IgnoreCase); - if (MainIncludeRegex.match(FileStem)) - return true; - } - return false; -} - -HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, - const IncludeStyle &Style) - : FileName(FileName), Code(Code), FirstIncludeOffset(-1), - MinInsertOffset( - getOffsetAfterHeaderGuardsAndComments(FileName, Code, Style)), - MaxInsertOffset(MinInsertOffset + - getMaxHeaderInsertionOffset( - FileName, Code.drop_front(MinInsertOffset), Style)), - Categories(Style, FileName), - IncludeRegex(llvm::Regex(IncludeRegexPattern)) { - // Add 0 for main header and INT_MAX for headers that are not in any - // category. - Priorities = {0, INT_MAX}; - for (const auto &Category : Style.IncludeCategories) - Priorities.insert(Category.Priority); - SmallVector Lines; - Code.drop_front(MinInsertOffset).split(Lines, "\n"); - - unsigned Offset = MinInsertOffset; - unsigned NextLineOffset; - SmallVector Matches; - for (auto Line : Lines) { - NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1); - if (IncludeRegex.match(Line, &Matches)) { - // If this is the last line without trailing newline, we need to make - // sure we don't delete across the file boundary. - addExistingInclude( - Include(Matches[2], - tooling::Range( - Offset, std::min(Line.size() + 1, Code.size() - Offset))), - NextLineOffset); - } - Offset = NextLineOffset; - } - - // Populate CategoryEndOfssets: - // - Ensure that CategoryEndOffset[Highest] is always populated. - // - If CategoryEndOffset[Priority] isn't set, use the next higher value - // that is set, up to CategoryEndOffset[Highest]. - auto Highest = Priorities.begin(); - if (CategoryEndOffsets.find(*Highest) == CategoryEndOffsets.end()) { - if (FirstIncludeOffset >= 0) - CategoryEndOffsets[*Highest] = FirstIncludeOffset; - else - CategoryEndOffsets[*Highest] = MinInsertOffset; - } - // By this point, CategoryEndOffset[Highest] is always set appropriately: - // - to an appropriate location before/after existing #includes, or - // - to right after the header guard, or - // - to the beginning of the file. - for (auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I) - if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end()) - CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)]; -} - -// \p Offset: the start of the line following this include directive. -void HeaderIncludes::addExistingInclude(Include IncludeToAdd, - unsigned NextLineOffset) { - auto Iter = - ExistingIncludes.try_emplace(trimInclude(IncludeToAdd.Name)).first; - Iter->second.push_back(std::move(IncludeToAdd)); - auto &CurInclude = Iter->second.back(); - // The header name with quotes or angle brackets. - // Only record the offset of current #include if we can insert after it. - if (CurInclude.R.getOffset() <= MaxInsertOffset) { - int Priority = Categories.getIncludePriority( - CurInclude.Name, /*CheckMainHeader=*/FirstIncludeOffset < 0); - CategoryEndOffsets[Priority] = NextLineOffset; - IncludesByPriority[Priority].push_back(&CurInclude); - if (FirstIncludeOffset < 0) - FirstIncludeOffset = CurInclude.R.getOffset(); - } -} - -llvm::Optional -HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const { - assert(IncludeName == trimInclude(IncludeName)); - // If a
("header") already exists in code, "header" (
) with - // different quotation will still be inserted. - // FIXME: figure out if this is the best behavior. - auto It = ExistingIncludes.find(IncludeName); - if (It != ExistingIncludes.end()) - for (const auto &Inc : It->second) - if ((IsAngled && StringRef(Inc.Name).startswith("<")) || - (!IsAngled && StringRef(Inc.Name).startswith("\""))) - return llvm::None; - std::string Quoted = IsAngled ? ("<" + IncludeName + ">").str() - : ("\"" + IncludeName + "\"").str(); - StringRef QuotedName = Quoted; - int Priority = Categories.getIncludePriority( - QuotedName, /*CheckMainHeader=*/FirstIncludeOffset < 0); - auto CatOffset = CategoryEndOffsets.find(Priority); - assert(CatOffset != CategoryEndOffsets.end()); - unsigned InsertOffset = CatOffset->second; // Fall back offset - auto Iter = IncludesByPriority.find(Priority); - if (Iter != IncludesByPriority.end()) { - for (const auto *Inc : Iter->second) { - if (QuotedName < Inc->Name) { - InsertOffset = Inc->R.getOffset(); - break; - } - } - } - assert(InsertOffset <= Code.size()); - std::string NewInclude = ("#include " + QuotedName + "\n").str(); - // When inserting headers at end of the code, also append '\n' to the code - // if it does not end with '\n'. - // FIXME: when inserting multiple #includes at the end of code, only one - // newline should be added. - if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n')) - NewInclude = "\n" + NewInclude; - return tooling::Replacement(FileName, InsertOffset, 0, NewInclude); -} - -tooling::Replacements HeaderIncludes::remove(llvm::StringRef IncludeName, - bool IsAngled) const { - assert(IncludeName == trimInclude(IncludeName)); - tooling::Replacements Result; - auto Iter = ExistingIncludes.find(IncludeName); - if (Iter == ExistingIncludes.end()) - return Result; - for (const auto &Inc : Iter->second) { - if ((IsAngled && StringRef(Inc.Name).startswith("\"")) || - (!IsAngled && StringRef(Inc.Name).startswith("<"))) - continue; - llvm::Error Err = Result.add(tooling::Replacement( - FileName, Inc.R.getOffset(), Inc.R.getLength(), "")); - if (Err) { - auto ErrMsg = "Unexpected conflicts in #include deletions: " + - llvm::toString(std::move(Err)); - llvm_unreachable(ErrMsg.c_str()); - } - } - return Result; -} - - -} // namespace tooling -} // namespace clang Index: cfe/trunk/lib/Tooling/Core/IncludeStyle.cpp =================================================================== --- cfe/trunk/lib/Tooling/Core/IncludeStyle.cpp +++ cfe/trunk/lib/Tooling/Core/IncludeStyle.cpp @@ -1,32 +0,0 @@ -//===--- IncludeStyle.cpp - Style of C++ #include directives -----*- C++-*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Tooling/Core/IncludeStyle.h" - - -using clang::tooling::IncludeStyle; - -namespace llvm { -namespace yaml { - -void MappingTraits::mapping( - IO &IO, IncludeStyle::IncludeCategory &Category) { - IO.mapOptional("Regex", Category.Regex); - IO.mapOptional("Priority", Category.Priority); -} - -void ScalarEnumerationTraits::enumeration( - IO &IO, IncludeStyle::IncludeBlocksStyle &Value) { - IO.enumCase(Value, "Preserve", IncludeStyle::IBS_Preserve); - IO.enumCase(Value, "Merge", IncludeStyle::IBS_Merge); - IO.enumCase(Value, "Regroup", IncludeStyle::IBS_Regroup); -} - -} // namespace yaml -} // namespace llvm Index: cfe/trunk/lib/Tooling/Inclusions/CMakeLists.txt =================================================================== --- cfe/trunk/lib/Tooling/Inclusions/CMakeLists.txt +++ cfe/trunk/lib/Tooling/Inclusions/CMakeLists.txt @@ -0,0 +1,12 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangToolingInclusions + HeaderIncludes.cpp + IncludeStyle.cpp + + LINK_LIBS + clangBasic + clangLex + clangRewrite + clangToolingCore + ) Index: cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp =================================================================== --- cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ cfe/trunk/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -0,0 +1,330 @@ +//===--- HeaderIncludes.cpp - Insert/Delete #includes --*- C++ -*----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Inclusions/HeaderIncludes.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace tooling { +namespace { + +LangOptions createLangOpts() { + LangOptions LangOpts; + LangOpts.CPlusPlus = 1; + LangOpts.CPlusPlus11 = 1; + LangOpts.CPlusPlus14 = 1; + LangOpts.LineComment = 1; + LangOpts.CXXOperatorNames = 1; + LangOpts.Bool = 1; + LangOpts.ObjC1 = 1; + LangOpts.ObjC2 = 1; + LangOpts.MicrosoftExt = 1; // To get kw___try, kw___finally. + LangOpts.DeclSpecKeyword = 1; // To get __declspec. + LangOpts.WChar = 1; // To get wchar_t + return LangOpts; +} + +// Returns the offset after skipping a sequence of tokens, matched by \p +// GetOffsetAfterSequence, from the start of the code. +// \p GetOffsetAfterSequence should be a function that matches a sequence of +// tokens and returns an offset after the sequence. +unsigned getOffsetAfterTokenSequence( + StringRef FileName, StringRef Code, const IncludeStyle &Style, + llvm::function_ref + GetOffsetAfterSequence) { + SourceManagerForFile VirtualSM(FileName, Code); + SourceManager &SM = VirtualSM.get(); + Lexer Lex(SM.getMainFileID(), SM.getBuffer(SM.getMainFileID()), SM, + createLangOpts()); + Token Tok; + // Get the first token. + Lex.LexFromRawLexer(Tok); + return GetOffsetAfterSequence(SM, Lex, Tok); +} + +// Check if a sequence of tokens is like "# ". If it is, +// \p Tok will be the token after this directive; otherwise, it can be any token +// after the given \p Tok (including \p Tok). +bool checkAndConsumeDirectiveWithName(Lexer &Lex, StringRef Name, Token &Tok) { + bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && + Tok.is(tok::raw_identifier) && + Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) && + Tok.is(tok::raw_identifier); + if (Matched) + Lex.LexFromRawLexer(Tok); + return Matched; +} + +void skipComments(Lexer &Lex, Token &Tok) { + while (Tok.is(tok::comment)) + if (Lex.LexFromRawLexer(Tok)) + return; +} + +// Returns the offset after header guard directives and any comments +// before/after header guards. If no header guard presents in the code, this +// will returns the offset after skipping all comments from the start of the +// code. +unsigned getOffsetAfterHeaderGuardsAndComments(StringRef FileName, + StringRef Code, + const IncludeStyle &Style) { + return getOffsetAfterTokenSequence( + FileName, Code, Style, + [](const SourceManager &SM, Lexer &Lex, Token Tok) { + skipComments(Lex, Tok); + unsigned InitialOffset = SM.getFileOffset(Tok.getLocation()); + if (checkAndConsumeDirectiveWithName(Lex, "ifndef", Tok)) { + skipComments(Lex, Tok); + if (checkAndConsumeDirectiveWithName(Lex, "define", Tok)) + return SM.getFileOffset(Tok.getLocation()); + } + return InitialOffset; + }); +} + +// Check if a sequence of tokens is like +// "#include ("header.h" | )". +// If it is, \p Tok will be the token after this directive; otherwise, it can be +// any token after the given \p Tok (including \p Tok). +bool checkAndConsumeInclusiveDirective(Lexer &Lex, Token &Tok) { + auto Matched = [&]() { + Lex.LexFromRawLexer(Tok); + return true; + }; + if (Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && + Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "include") { + if (Lex.LexFromRawLexer(Tok)) + return false; + if (Tok.is(tok::string_literal)) + return Matched(); + if (Tok.is(tok::less)) { + while (!Lex.LexFromRawLexer(Tok) && Tok.isNot(tok::greater)) { + } + if (Tok.is(tok::greater)) + return Matched(); + } + } + return false; +} + +// Returns the offset of the last #include directive after which a new +// #include can be inserted. This ignores #include's after the #include block(s) +// in the beginning of a file to avoid inserting headers into code sections +// where new #include's should not be added by default. +// These code sections include: +// - raw string literals (containing #include). +// - #if blocks. +// - Special #include's among declarations (e.g. functions). +// +// If no #include after which a new #include can be inserted, this returns the +// offset after skipping all comments from the start of the code. +// Inserting after an #include is not allowed if it comes after code that is not +// #include (e.g. pre-processing directive that is not #include, declarations). +unsigned getMaxHeaderInsertionOffset(StringRef FileName, StringRef Code, + const IncludeStyle &Style) { + return getOffsetAfterTokenSequence( + FileName, Code, Style, + [](const SourceManager &SM, Lexer &Lex, Token Tok) { + skipComments(Lex, Tok); + unsigned MaxOffset = SM.getFileOffset(Tok.getLocation()); + while (checkAndConsumeInclusiveDirective(Lex, Tok)) + MaxOffset = SM.getFileOffset(Tok.getLocation()); + return MaxOffset; + }); +} + +inline StringRef trimInclude(StringRef IncludeName) { + return IncludeName.trim("\"<>"); +} + +const char IncludeRegexPattern[] = + R"(^[\t\ ]*#[\t\ ]*(import|include)[^"<]*(["<][^">]*[">]))"; + +} // anonymous namespace + +IncludeCategoryManager::IncludeCategoryManager(const IncludeStyle &Style, + StringRef FileName) + : Style(Style), FileName(FileName) { + FileStem = llvm::sys::path::stem(FileName); + for (const auto &Category : Style.IncludeCategories) + CategoryRegexs.emplace_back(Category.Regex, llvm::Regex::IgnoreCase); + IsMainFile = FileName.endswith(".c") || FileName.endswith(".cc") || + FileName.endswith(".cpp") || FileName.endswith(".c++") || + FileName.endswith(".cxx") || FileName.endswith(".m") || + FileName.endswith(".mm"); +} + +int IncludeCategoryManager::getIncludePriority(StringRef IncludeName, + bool CheckMainHeader) const { + int Ret = INT_MAX; + for (unsigned i = 0, e = CategoryRegexs.size(); i != e; ++i) + if (CategoryRegexs[i].match(IncludeName)) { + Ret = Style.IncludeCategories[i].Priority; + break; + } + if (CheckMainHeader && IsMainFile && Ret > 0 && isMainHeader(IncludeName)) + Ret = 0; + return Ret; +} + +bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { + if (!IncludeName.startswith("\"")) + return false; + StringRef HeaderStem = + llvm::sys::path::stem(IncludeName.drop_front(1).drop_back(1)); + if (FileStem.startswith(HeaderStem) || + FileStem.startswith_lower(HeaderStem)) { + llvm::Regex MainIncludeRegex((HeaderStem + Style.IncludeIsMainRegex).str(), + llvm::Regex::IgnoreCase); + if (MainIncludeRegex.match(FileStem)) + return true; + } + return false; +} + +HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, + const IncludeStyle &Style) + : FileName(FileName), Code(Code), FirstIncludeOffset(-1), + MinInsertOffset( + getOffsetAfterHeaderGuardsAndComments(FileName, Code, Style)), + MaxInsertOffset(MinInsertOffset + + getMaxHeaderInsertionOffset( + FileName, Code.drop_front(MinInsertOffset), Style)), + Categories(Style, FileName), + IncludeRegex(llvm::Regex(IncludeRegexPattern)) { + // Add 0 for main header and INT_MAX for headers that are not in any + // category. + Priorities = {0, INT_MAX}; + for (const auto &Category : Style.IncludeCategories) + Priorities.insert(Category.Priority); + SmallVector Lines; + Code.drop_front(MinInsertOffset).split(Lines, "\n"); + + unsigned Offset = MinInsertOffset; + unsigned NextLineOffset; + SmallVector Matches; + for (auto Line : Lines) { + NextLineOffset = std::min(Code.size(), Offset + Line.size() + 1); + if (IncludeRegex.match(Line, &Matches)) { + // If this is the last line without trailing newline, we need to make + // sure we don't delete across the file boundary. + addExistingInclude( + Include(Matches[2], + tooling::Range( + Offset, std::min(Line.size() + 1, Code.size() - Offset))), + NextLineOffset); + } + Offset = NextLineOffset; + } + + // Populate CategoryEndOfssets: + // - Ensure that CategoryEndOffset[Highest] is always populated. + // - If CategoryEndOffset[Priority] isn't set, use the next higher value + // that is set, up to CategoryEndOffset[Highest]. + auto Highest = Priorities.begin(); + if (CategoryEndOffsets.find(*Highest) == CategoryEndOffsets.end()) { + if (FirstIncludeOffset >= 0) + CategoryEndOffsets[*Highest] = FirstIncludeOffset; + else + CategoryEndOffsets[*Highest] = MinInsertOffset; + } + // By this point, CategoryEndOffset[Highest] is always set appropriately: + // - to an appropriate location before/after existing #includes, or + // - to right after the header guard, or + // - to the beginning of the file. + for (auto I = ++Priorities.begin(), E = Priorities.end(); I != E; ++I) + if (CategoryEndOffsets.find(*I) == CategoryEndOffsets.end()) + CategoryEndOffsets[*I] = CategoryEndOffsets[*std::prev(I)]; +} + +// \p Offset: the start of the line following this include directive. +void HeaderIncludes::addExistingInclude(Include IncludeToAdd, + unsigned NextLineOffset) { + auto Iter = + ExistingIncludes.try_emplace(trimInclude(IncludeToAdd.Name)).first; + Iter->second.push_back(std::move(IncludeToAdd)); + auto &CurInclude = Iter->second.back(); + // The header name with quotes or angle brackets. + // Only record the offset of current #include if we can insert after it. + if (CurInclude.R.getOffset() <= MaxInsertOffset) { + int Priority = Categories.getIncludePriority( + CurInclude.Name, /*CheckMainHeader=*/FirstIncludeOffset < 0); + CategoryEndOffsets[Priority] = NextLineOffset; + IncludesByPriority[Priority].push_back(&CurInclude); + if (FirstIncludeOffset < 0) + FirstIncludeOffset = CurInclude.R.getOffset(); + } +} + +llvm::Optional +HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const { + assert(IncludeName == trimInclude(IncludeName)); + // If a
("header") already exists in code, "header" (
) with + // different quotation will still be inserted. + // FIXME: figure out if this is the best behavior. + auto It = ExistingIncludes.find(IncludeName); + if (It != ExistingIncludes.end()) + for (const auto &Inc : It->second) + if ((IsAngled && StringRef(Inc.Name).startswith("<")) || + (!IsAngled && StringRef(Inc.Name).startswith("\""))) + return llvm::None; + std::string Quoted = IsAngled ? ("<" + IncludeName + ">").str() + : ("\"" + IncludeName + "\"").str(); + StringRef QuotedName = Quoted; + int Priority = Categories.getIncludePriority( + QuotedName, /*CheckMainHeader=*/FirstIncludeOffset < 0); + auto CatOffset = CategoryEndOffsets.find(Priority); + assert(CatOffset != CategoryEndOffsets.end()); + unsigned InsertOffset = CatOffset->second; // Fall back offset + auto Iter = IncludesByPriority.find(Priority); + if (Iter != IncludesByPriority.end()) { + for (const auto *Inc : Iter->second) { + if (QuotedName < Inc->Name) { + InsertOffset = Inc->R.getOffset(); + break; + } + } + } + assert(InsertOffset <= Code.size()); + std::string NewInclude = ("#include " + QuotedName + "\n").str(); + // When inserting headers at end of the code, also append '\n' to the code + // if it does not end with '\n'. + // FIXME: when inserting multiple #includes at the end of code, only one + // newline should be added. + if (InsertOffset == Code.size() && (!Code.empty() && Code.back() != '\n')) + NewInclude = "\n" + NewInclude; + return tooling::Replacement(FileName, InsertOffset, 0, NewInclude); +} + +tooling::Replacements HeaderIncludes::remove(llvm::StringRef IncludeName, + bool IsAngled) const { + assert(IncludeName == trimInclude(IncludeName)); + tooling::Replacements Result; + auto Iter = ExistingIncludes.find(IncludeName); + if (Iter == ExistingIncludes.end()) + return Result; + for (const auto &Inc : Iter->second) { + if ((IsAngled && StringRef(Inc.Name).startswith("\"")) || + (!IsAngled && StringRef(Inc.Name).startswith("<"))) + continue; + llvm::Error Err = Result.add(tooling::Replacement( + FileName, Inc.R.getOffset(), Inc.R.getLength(), "")); + if (Err) { + auto ErrMsg = "Unexpected conflicts in #include deletions: " + + llvm::toString(std::move(Err)); + llvm_unreachable(ErrMsg.c_str()); + } + } + return Result; +} + + +} // namespace tooling +} // namespace clang Index: cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp =================================================================== --- cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp +++ cfe/trunk/lib/Tooling/Inclusions/IncludeStyle.cpp @@ -0,0 +1,31 @@ +//===--- IncludeStyle.cpp - Style of C++ #include directives -----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Inclusions/IncludeStyle.h" + +using clang::tooling::IncludeStyle; + +namespace llvm { +namespace yaml { + +void MappingTraits::mapping( + IO &IO, IncludeStyle::IncludeCategory &Category) { + IO.mapOptional("Regex", Category.Regex); + IO.mapOptional("Priority", Category.Priority); +} + +void ScalarEnumerationTraits::enumeration( + IO &IO, IncludeStyle::IncludeBlocksStyle &Value) { + IO.enumCase(Value, "Preserve", IncludeStyle::IBS_Preserve); + IO.enumCase(Value, "Merge", IncludeStyle::IBS_Merge); + IO.enumCase(Value, "Regroup", IncludeStyle::IBS_Regroup); +} + +} // namespace yaml +} // namespace llvm Index: cfe/trunk/unittests/Tooling/HeaderIncludesTest.cpp =================================================================== --- cfe/trunk/unittests/Tooling/HeaderIncludesTest.cpp +++ cfe/trunk/unittests/Tooling/HeaderIncludesTest.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/Tooling/Core/HeaderIncludes.h" +#include "clang/Tooling/Inclusions/HeaderIncludes.h" #include "../Tooling/ReplacementTest.h" #include "../Tooling/RewriterTestContext.h" #include "clang/Format/Format.h"