/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 |