xref: /llvm-project/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp (revision 99b5ec1fd1a70680a8483e5efb86807254e44e0e)
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.startswith("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.startswith("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(llvm::MemoryBufferRef Buffer) {
41   StringRef Content = Buffer.getBuffer();
42   llvm::StringRef Line;
43   // Only sniff up to 100 lines or 10KB.
44   Content = Content.take_front(100 * 100);
45   for (unsigned I = 0; I < 100 && !Content.empty(); ++I) {
46     std::tie(Line, Content) = Content.split('\n');
47     if (isIf(Line) && isErrorAboutInclude(Content.split('\n').first))
48       return true;
49   }
50   return false;
51 }
52 
53 } // namespace
54 
55 bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM,
56                            HeaderSearch &HeaderInfo) {
57   assert(FE);
58   if (!HeaderInfo.isFileMultipleIncludeGuarded(FE) &&
59       !HeaderInfo.hasFileBeenImported(FE))
60     return false;
61   // This pattern indicates that a header can't be used without
62   // particular preprocessor state, usually set up by another header.
63   return !isDontIncludeMeHeader(
64       const_cast<SourceManager &>(SM).getMemoryBufferForFileOrNone(FE).value_or(
65           llvm::MemoryBufferRef()));
66 }
67 
68 llvm::Optional<StringRef> parseIWYUPragma(const char *Text) {
69   // Skip the comment start, // or /*.
70   if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*'))
71     return llvm::None;
72   bool BlockComment = Text[1] == '*';
73   Text += 2;
74 
75   // Per spec, direcitves are whitespace- and case-sensitive.
76   constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
77   if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
78     return llvm::None;
79   Text += IWYUPragma.size();
80   const char *End = Text;
81   while (*End != 0 && *End != '\n')
82     ++End;
83   StringRef Rest(Text, End - Text);
84   // Strip off whitespace and comment markers to avoid confusion. This isn't
85   // fully-compatible with IWYU, which splits into whitespace-delimited tokens.
86   if (BlockComment)
87     Rest.consume_back("*/");
88   return Rest.trim();
89 }
90 
91 } // namespace clang::tooling
92