Coverage Report

Created: 2023-05-31 04:38

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