aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Tooling/Inclusions
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Tooling/Inclusions')
-rw-r--r--clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp119
-rw-r--r--clang/lib/Tooling/Inclusions/HeaderIncludes.cpp34
-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);
}