xref: /llvm-project/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp (revision dd46a08008f76d6ac9fcc6a9e748b113bea3c758)
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 } // namespace clang::tooling
68