diff options
Diffstat (limited to 'clang/lib/Tooling/Inclusions')
-rw-r--r-- | clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp | 119 | ||||
-rw-r--r-- | clang/lib/Tooling/Inclusions/HeaderIncludes.cpp | 34 | ||||
-rw-r--r-- | clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp (renamed from clang/lib/Tooling/Inclusions/StandardLibrary.cpp) | 18 |
3 files changed, 149 insertions, 22 deletions
diff --git a/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp new file mode 100644 index 000000000000..49d23908d33b --- /dev/null +++ b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp @@ -0,0 +1,119 @@ +//===--- HeaderAnalysis.cpp -------------------------------------*- C++ -*-===// +// +// 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 "clang/Tooling/Inclusions/HeaderAnalysis.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Lex/HeaderSearch.h" + +namespace clang::tooling { +namespace { + +// Is Line an #if or #ifdef directive? +// FIXME: This makes headers with #ifdef LINUX/WINDOWS/MACOS marked as non +// self-contained and is probably not what we want. +bool isIf(llvm::StringRef Line) { + Line = Line.ltrim(); + if (!Line.consume_front("#")) + return false; + Line = Line.ltrim(); + return Line.startswith("if"); +} + +// Is Line an #error directive mentioning includes? +bool isErrorAboutInclude(llvm::StringRef Line) { + Line = Line.ltrim(); + if (!Line.consume_front("#")) + return false; + Line = Line.ltrim(); + if (!Line.startswith("error")) + return false; + return Line.contains_insensitive( + "includ"); // Matches "include" or "including". +} + +// Heuristically headers that only want to be included via an umbrella. +bool isDontIncludeMeHeader(StringRef Content) { + llvm::StringRef Line; + // Only sniff up to 100 lines or 10KB. + Content = Content.take_front(100 * 100); + for (unsigned I = 0; I < 100 && !Content.empty(); ++I) { + std::tie(Line, Content) = Content.split('\n'); + if (isIf(Line) && isErrorAboutInclude(Content.split('\n').first)) + return true; + } + return false; +} + +bool isImportLine(llvm::StringRef Line) { + Line = Line.ltrim(); + if (!Line.consume_front("#")) + return false; + Line = Line.ltrim(); + return Line.startswith("import"); +} + +llvm::StringRef getFileContents(const FileEntry *FE, const SourceManager &SM) { + return const_cast<SourceManager &>(SM) + .getMemoryBufferForFileOrNone(FE) + .value_or(llvm::MemoryBufferRef()) + .getBuffer(); +} + +} // namespace + +bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM, + HeaderSearch &HeaderInfo) { + assert(FE); + if (!HeaderInfo.isFileMultipleIncludeGuarded(FE) && + !HeaderInfo.hasFileBeenImported(FE) && + // Any header that contains #imports is supposed to be #import'd so no + // need to check for anything but the main-file. + (SM.getFileEntryForID(SM.getMainFileID()) != FE || + !codeContainsImports(getFileContents(FE, SM)))) + return false; + // This pattern indicates that a header can't be used without + // particular preprocessor state, usually set up by another header. + return !isDontIncludeMeHeader(getFileContents(FE, SM)); +} + +bool codeContainsImports(llvm::StringRef Code) { + // Only sniff up to 100 lines or 10KB. + Code = Code.take_front(100 * 100); + llvm::StringRef Line; + for (unsigned I = 0; I < 100 && !Code.empty(); ++I) { + std::tie(Line, Code) = Code.split('\n'); + if (isImportLine(Line)) + return true; + } + return false; +} + +std::optional<StringRef> parseIWYUPragma(const char *Text) { + // Skip the comment start, // or /*. + if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*')) + return std::nullopt; + bool BlockComment = Text[1] == '*'; + Text += 2; + + // Per spec, direcitves are whitespace- and case-sensitive. + constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: "; + if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size())) + return std::nullopt; + Text += IWYUPragma.size(); + const char *End = Text; + while (*End != 0 && *End != '\n') + ++End; + StringRef Rest(Text, End - Text); + // Strip off whitespace and comment markers to avoid confusion. This isn't + // fully-compatible with IWYU, which splits into whitespace-delimited tokens. + if (BlockComment) + Rest.consume_back("*/"); + return Rest.trim(); +} + +} // namespace clang::tooling diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp index fc8773e60c58..172eff1bf6ab 100644 --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -10,9 +10,9 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" -#include "llvm/ADT/Optional.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" +#include <optional> namespace clang { namespace tooling { @@ -58,7 +58,7 @@ unsigned getOffsetAfterTokenSequence( // (second) raw_identifier name is checked. bool checkAndConsumeDirectiveWithName( Lexer &Lex, StringRef Name, Token &Tok, - llvm::Optional<StringRef> RawIDName = llvm::None) { + std::optional<StringRef> RawIDName = std::nullopt) { bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) && Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) && @@ -266,6 +266,8 @@ bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const { return false; } +const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern); + HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, const IncludeStyle &Style) : FileName(FileName), Code(Code), FirstIncludeOffset(-1), @@ -274,8 +276,7 @@ HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, MaxInsertOffset(MinInsertOffset + getMaxHeaderInsertionOffset( FileName, Code.drop_front(MinInsertOffset), Style)), - Categories(Style, FileName), - IncludeRegex(llvm::Regex(IncludeRegexPattern)) { + Categories(Style, FileName) { // Add 0 for main header and INT_MAX for headers that are not in any // category. Priorities = {0, INT_MAX}; @@ -295,7 +296,9 @@ HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code, addExistingInclude( Include(Matches[2], tooling::Range( - Offset, std::min(Line.size() + 1, Code.size() - Offset))), + Offset, std::min(Line.size() + 1, Code.size() - Offset)), + Matches[1] == "import" ? tooling::IncludeDirective::Import + : tooling::IncludeDirective::Include), NextLineOffset); } Offset = NextLineOffset; @@ -340,18 +343,21 @@ void HeaderIncludes::addExistingInclude(Include IncludeToAdd, } } -llvm::Optional<tooling::Replacement> -HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const { +std::optional<tooling::Replacement> +HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled, + IncludeDirective Directive) const { assert(IncludeName == trimInclude(IncludeName)); // If a <header> ("header") already exists in code, "header" (<header>) with - // different quotation will still be inserted. + // different quotation and/or directive will still be inserted. // FIXME: figure out if this is the best behavior. auto It = ExistingIncludes.find(IncludeName); - if (It != ExistingIncludes.end()) + if (It != ExistingIncludes.end()) { for (const auto &Inc : It->second) - if ((IsAngled && StringRef(Inc.Name).startswith("<")) || - (!IsAngled && StringRef(Inc.Name).startswith("\""))) - return llvm::None; + if (Inc.Directive == Directive && + ((IsAngled && StringRef(Inc.Name).startswith("<")) || + (!IsAngled && StringRef(Inc.Name).startswith("\"")))) + return std::nullopt; + } std::string Quoted = std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", IncludeName)); StringRef QuotedName = Quoted; @@ -370,8 +376,10 @@ HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const { } } assert(InsertOffset <= Code.size()); + llvm::StringRef DirectiveSpelling = + Directive == IncludeDirective::Include ? "include" : "import"; std::string NewInclude = - std::string(llvm::formatv("#include {0}\n", QuotedName)); + llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName); // 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 diff --git a/clang/lib/Tooling/Inclusions/StandardLibrary.cpp b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp index 8fb0c8474e64..9e5e421fdebc 100644 --- a/clang/lib/Tooling/Inclusions/StandardLibrary.cpp +++ b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Inclusions/StandardLibrary.h" -#include "llvm/ADT/Optional.h" +#include "clang/AST/Decl.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" @@ -76,17 +76,17 @@ static void ensureInitialized() { (void)Dummy; } -llvm::Optional<Header> Header::named(llvm::StringRef Name) { +std::optional<Header> Header::named(llvm::StringRef Name) { ensureInitialized(); auto It = HeaderIDs->find(Name); if (It == HeaderIDs->end()) - return llvm::None; + return std::nullopt; return Header(It->second); } llvm::StringRef Header::name() const { return HeaderNames[ID]; } llvm::StringRef Symbol::scope() const { return SymbolNames[ID].first; } llvm::StringRef Symbol::name() const { return SymbolNames[ID].second; } -llvm::Optional<Symbol> Symbol::named(llvm::StringRef Scope, +std::optional<Symbol> Symbol::named(llvm::StringRef Scope, llvm::StringRef Name) { ensureInitialized(); if (NSSymbolMap *NSSymbols = NamespaceSymbols->lookup(Scope)) { @@ -94,7 +94,7 @@ llvm::Optional<Symbol> Symbol::named(llvm::StringRef Scope, if (It != NSSymbols->end()) return Symbol(It->second); } - return llvm::None; + return std::nullopt; } Header Symbol::header() const { return Header(SymbolHeaderIDs[ID]); } llvm::SmallVector<Header> Symbol::headers() const { @@ -123,7 +123,7 @@ NSSymbolMap *Recognizer::namespaceSymbols(const NamespaceDecl *D) { return Result; } -llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) { +std::optional<Symbol> Recognizer::operator()(const Decl *D) { // If D is std::vector::iterator, `vector` is the outer symbol to look up. // We keep all the candidate DCs as some may turn out to be anon enums. // Do this resolution lazily as we may turn out not to have a std namespace. @@ -136,7 +136,7 @@ llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) { } NSSymbolMap *Symbols = namespaceSymbols(cast_or_null<NamespaceDecl>(DC)); if (!Symbols) - return llvm::None; + return std::nullopt; llvm::StringRef Name = [&]() -> llvm::StringRef { for (const auto *SymDC : llvm::reverse(IntermediateDecl)) { @@ -152,11 +152,11 @@ llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) { return ""; }(); if (Name.empty()) - return llvm::None; + return std::nullopt; auto It = Symbols->find(Name); if (It == Symbols->end()) - return llvm::None; + return std::nullopt; return Symbol(It->second); } |