Mercurial > hg > CbC > CbC_llvm
view clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | 0572611fdcc8 |
children | c4bab56944e8 |
line wrap: on
line source
//===---------- IncludeSorter.cpp - clang-tidy ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "IncludeSorter.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include <algorithm> namespace clang { namespace tidy { namespace utils { namespace { StringRef removeFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) { for (StringRef Suffix : Suffixes) { if (Str.endswith(Suffix)) { return Str.substr(0, Str.size() - Suffix.size()); } } return Str; } StringRef makeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) { // The list of suffixes to remove from source file names to get the // "canonical" file names. // E.g. tools/sort_includes.cc and tools/sort_includes_test.cc // would both canonicalize to tools/sort_includes and tools/sort_includes.h // (once canonicalized) will match as being the main include file associated // with the source files. if (Style == IncludeSorter::IS_LLVM) { return removeFirstSuffix( removeFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"}); } if (Style == IncludeSorter::IS_Google_ObjC) { StringRef Canonical = removeFirstSuffix(removeFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp", ".mm", ".m"}), {"_unittest", "_regtest", "_test", "Test"}); // Objective-C categories have a `+suffix` format, but should be grouped // with the file they are a category of. size_t StartIndex = Canonical.find_last_of('/'); if (StartIndex == StringRef::npos) { StartIndex = 0; } return Canonical.substr( 0, Canonical.find_first_of('+', StartIndex)); } return removeFirstSuffix( removeFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"_unittest", "_regtest", "_test"}); } // Scan to the end of the line and return the offset of the next line. size_t findNextLine(const char *Text) { size_t EOLIndex = std::strcspn(Text, "\n"); return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1; } IncludeSorter::IncludeKinds determineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile, bool IsAngled, IncludeSorter::IncludeStyle Style) { // Compute the two "canonical" forms of the include's filename sans extension. // The first form is the include's filename without ".h" or "-inl.h" at the // end. The second form is the first form with "/public/" in the file path // replaced by "/internal/". if (IsAngled) { // If the system include (<foo>) ends with ".h", then it is a normal C-style // include. Otherwise assume it is a C++-style extensionless include. return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude : IncludeSorter::IK_CXXSystemInclude; } StringRef CanonicalInclude = makeCanonicalName(IncludeFile, Style); if (CanonicalFile.endswith(CanonicalInclude) || CanonicalInclude.endswith(CanonicalFile)) { return IncludeSorter::IK_MainTUInclude; } if ((Style == IncludeSorter::IS_Google) || (Style == IncludeSorter::IS_Google_ObjC)) { 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; } } if (Style == IncludeSorter::IS_Google_ObjC) { if (IncludeFile.endswith(".generated.h") || IncludeFile.endswith(".proto.h") || IncludeFile.endswith(".pbobjc.h")) { return IncludeSorter::IK_GeneratedInclude; } } return IncludeSorter::IK_NonSystemInclude; } int compareHeaders(StringRef LHS, StringRef RHS, IncludeSorter::IncludeStyle Style) { if (Style == IncludeSorter::IncludeStyle::IS_Google_ObjC) { const std::pair<const char *, const char *> &Mismatch = std::mismatch(LHS.begin(), LHS.end(), RHS.begin()); if ((Mismatch.first != LHS.end()) && (Mismatch.second != RHS.end())) { if ((*Mismatch.first == '.') && (*Mismatch.second == '+')) { return -1; } if ((*Mismatch.first == '+') && (*Mismatch.second == '.')) { return 1; } } } return LHS.compare(RHS); } } // namespace IncludeSorter::IncludeSorter(const SourceManager *SourceMgr, const FileID FileID, StringRef FileName, IncludeStyle Style) : SourceMgr(SourceMgr), Style(Style), CurrentFileID(FileID), CanonicalFile(makeCanonicalName(FileName, Style)) {} void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled, SourceLocation HashLocation, SourceLocation EndLocation) { int Offset = findNextLine(SourceMgr->getCharacterData(EndLocation)); // Record the relevant location information for this inclusion directive. IncludeLocations[FileName].push_back( SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset))); SourceLocations.push_back(IncludeLocations[FileName].back()); // Stop if this inclusion is a duplicate. if (IncludeLocations[FileName].size() > 1) return; // Add the included file's name to the appropriate bucket. IncludeKinds Kind = determineIncludeKind(CanonicalFile, FileName, IsAngled, Style); if (Kind != IK_InvalidInclude) IncludeBucket[Kind].push_back(FileName.str()); } Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName, bool IsAngled) { std::string IncludeStmt; if (Style == IncludeStyle::IS_Google_ObjC) { IncludeStmt = IsAngled ? llvm::Twine("#import <" + FileName + ">\n").str() : llvm::Twine("#import \"" + FileName + "\"\n").str(); } else { IncludeStmt = IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str() : llvm::Twine("#include \"" + FileName + "\"\n").str(); } if (SourceLocations.empty()) { // If there are no includes in this file, add it in the first line. // FIXME: insert after the file comment or the header guard, if present. IncludeStmt.append("\n"); return FixItHint::CreateInsertion( SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt); } auto IncludeKind = determineIncludeKind(CanonicalFile, FileName, IsAngled, Style); if (!IncludeBucket[IncludeKind].empty()) { for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) { if (compareHeaders(FileName, IncludeEntry, Style) < 0) { const auto &Location = IncludeLocations[IncludeEntry][0]; return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt); } if (FileName == IncludeEntry) { return llvm::None; } } // FileName comes after all include entries in bucket, insert it after // last. const std::string &LastInclude = IncludeBucket[IncludeKind].back(); SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back(); return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(), IncludeStmt); } // Find the non-empty include bucket to be sorted directly above // 'IncludeKind'. If such a bucket exists, we'll want to sort the include // after that bucket. If no such bucket exists, find the first non-empty // include bucket in the file. In that case, we'll want to sort the include // before that bucket. IncludeKinds NonEmptyKind = IK_InvalidInclude; for (int I = IK_InvalidInclude - 1; I >= 0; --I) { if (!IncludeBucket[I].empty()) { NonEmptyKind = static_cast<IncludeKinds>(I); if (NonEmptyKind < IncludeKind) break; } } if (NonEmptyKind == IK_InvalidInclude) { return llvm::None; } if (NonEmptyKind < IncludeKind) { // Create a block after. const std::string &LastInclude = IncludeBucket[NonEmptyKind].back(); SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back(); IncludeStmt = '\n' + IncludeStmt; return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(), IncludeStmt); } // Create a block before. const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0]; SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back(); IncludeStmt.append("\n"); return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(), IncludeStmt); } } // namespace utils llvm::ArrayRef<std::pair<utils::IncludeSorter::IncludeStyle, StringRef>> OptionEnumMapping<utils::IncludeSorter::IncludeStyle>::getEnumMapping() { static constexpr std::pair<utils::IncludeSorter::IncludeStyle, StringRef> Mapping[] = {{utils::IncludeSorter::IS_LLVM, "llvm"}, {utils::IncludeSorter::IS_Google, "google"}, {utils::IncludeSorter::IS_Google_ObjC, "google-objc"}}; return makeArrayRef(Mapping); } } // namespace tidy } // namespace clang