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