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