1*12c85518Srobert //===--- HeaderAnalysis.cpp -------------------------------------*- C++ -*-===//
2*12c85518Srobert //
3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information.
5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*12c85518Srobert //
7*12c85518Srobert //===----------------------------------------------------------------------===//
8*12c85518Srobert
9*12c85518Srobert #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
10*12c85518Srobert #include "clang/Basic/SourceLocation.h"
11*12c85518Srobert #include "clang/Lex/HeaderSearch.h"
12*12c85518Srobert
13*12c85518Srobert namespace clang::tooling {
14*12c85518Srobert namespace {
15*12c85518Srobert
16*12c85518Srobert // Is Line an #if or #ifdef directive?
17*12c85518Srobert // FIXME: This makes headers with #ifdef LINUX/WINDOWS/MACOS marked as non
18*12c85518Srobert // self-contained and is probably not what we want.
isIf(llvm::StringRef Line)19*12c85518Srobert bool isIf(llvm::StringRef Line) {
20*12c85518Srobert Line = Line.ltrim();
21*12c85518Srobert if (!Line.consume_front("#"))
22*12c85518Srobert return false;
23*12c85518Srobert Line = Line.ltrim();
24*12c85518Srobert return Line.startswith("if");
25*12c85518Srobert }
26*12c85518Srobert
27*12c85518Srobert // Is Line an #error directive mentioning includes?
isErrorAboutInclude(llvm::StringRef Line)28*12c85518Srobert bool isErrorAboutInclude(llvm::StringRef Line) {
29*12c85518Srobert Line = Line.ltrim();
30*12c85518Srobert if (!Line.consume_front("#"))
31*12c85518Srobert return false;
32*12c85518Srobert Line = Line.ltrim();
33*12c85518Srobert if (!Line.startswith("error"))
34*12c85518Srobert return false;
35*12c85518Srobert return Line.contains_insensitive(
36*12c85518Srobert "includ"); // Matches "include" or "including".
37*12c85518Srobert }
38*12c85518Srobert
39*12c85518Srobert // Heuristically headers that only want to be included via an umbrella.
isDontIncludeMeHeader(StringRef Content)40*12c85518Srobert bool isDontIncludeMeHeader(StringRef Content) {
41*12c85518Srobert llvm::StringRef Line;
42*12c85518Srobert // Only sniff up to 100 lines or 10KB.
43*12c85518Srobert Content = Content.take_front(100 * 100);
44*12c85518Srobert for (unsigned I = 0; I < 100 && !Content.empty(); ++I) {
45*12c85518Srobert std::tie(Line, Content) = Content.split('\n');
46*12c85518Srobert if (isIf(Line) && isErrorAboutInclude(Content.split('\n').first))
47*12c85518Srobert return true;
48*12c85518Srobert }
49*12c85518Srobert return false;
50*12c85518Srobert }
51*12c85518Srobert
isImportLine(llvm::StringRef Line)52*12c85518Srobert bool isImportLine(llvm::StringRef Line) {
53*12c85518Srobert Line = Line.ltrim();
54*12c85518Srobert if (!Line.consume_front("#"))
55*12c85518Srobert return false;
56*12c85518Srobert Line = Line.ltrim();
57*12c85518Srobert return Line.startswith("import");
58*12c85518Srobert }
59*12c85518Srobert
getFileContents(const FileEntry * FE,const SourceManager & SM)60*12c85518Srobert llvm::StringRef getFileContents(const FileEntry *FE, const SourceManager &SM) {
61*12c85518Srobert return const_cast<SourceManager &>(SM)
62*12c85518Srobert .getMemoryBufferForFileOrNone(FE)
63*12c85518Srobert .value_or(llvm::MemoryBufferRef())
64*12c85518Srobert .getBuffer();
65*12c85518Srobert }
66*12c85518Srobert
67*12c85518Srobert } // namespace
68*12c85518Srobert
isSelfContainedHeader(const FileEntry * FE,const SourceManager & SM,HeaderSearch & HeaderInfo)69*12c85518Srobert bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM,
70*12c85518Srobert HeaderSearch &HeaderInfo) {
71*12c85518Srobert assert(FE);
72*12c85518Srobert if (!HeaderInfo.isFileMultipleIncludeGuarded(FE) &&
73*12c85518Srobert !HeaderInfo.hasFileBeenImported(FE) &&
74*12c85518Srobert // Any header that contains #imports is supposed to be #import'd so no
75*12c85518Srobert // need to check for anything but the main-file.
76*12c85518Srobert (SM.getFileEntryForID(SM.getMainFileID()) != FE ||
77*12c85518Srobert !codeContainsImports(getFileContents(FE, SM))))
78*12c85518Srobert return false;
79*12c85518Srobert // This pattern indicates that a header can't be used without
80*12c85518Srobert // particular preprocessor state, usually set up by another header.
81*12c85518Srobert return !isDontIncludeMeHeader(getFileContents(FE, SM));
82*12c85518Srobert }
83*12c85518Srobert
codeContainsImports(llvm::StringRef Code)84*12c85518Srobert bool codeContainsImports(llvm::StringRef Code) {
85*12c85518Srobert // Only sniff up to 100 lines or 10KB.
86*12c85518Srobert Code = Code.take_front(100 * 100);
87*12c85518Srobert llvm::StringRef Line;
88*12c85518Srobert for (unsigned I = 0; I < 100 && !Code.empty(); ++I) {
89*12c85518Srobert std::tie(Line, Code) = Code.split('\n');
90*12c85518Srobert if (isImportLine(Line))
91*12c85518Srobert return true;
92*12c85518Srobert }
93*12c85518Srobert return false;
94*12c85518Srobert }
95*12c85518Srobert
parseIWYUPragma(const char * Text)96*12c85518Srobert std::optional<StringRef> parseIWYUPragma(const char *Text) {
97*12c85518Srobert // Skip the comment start, // or /*.
98*12c85518Srobert if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*'))
99*12c85518Srobert return std::nullopt;
100*12c85518Srobert bool BlockComment = Text[1] == '*';
101*12c85518Srobert Text += 2;
102*12c85518Srobert
103*12c85518Srobert // Per spec, direcitves are whitespace- and case-sensitive.
104*12c85518Srobert constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
105*12c85518Srobert if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
106*12c85518Srobert return std::nullopt;
107*12c85518Srobert Text += IWYUPragma.size();
108*12c85518Srobert const char *End = Text;
109*12c85518Srobert while (*End != 0 && *End != '\n')
110*12c85518Srobert ++End;
111*12c85518Srobert StringRef Rest(Text, End - Text);
112*12c85518Srobert // Strip off whitespace and comment markers to avoid confusion. This isn't
113*12c85518Srobert // fully-compatible with IWYU, which splits into whitespace-delimited tokens.
114*12c85518Srobert if (BlockComment)
115*12c85518Srobert Rest.consume_back("*/");
116*12c85518Srobert return Rest.trim();
117*12c85518Srobert }
118*12c85518Srobert
119*12c85518Srobert } // namespace clang::tooling
120