xref: /llvm-project/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp (revision f3dcc2351cff7b26c9870d737e5d431551542d9e)
1dd46a080SHaojian Wu //===--- HeaderAnalysis.cpp -------------------------------------*- C++ -*-===//
2dd46a080SHaojian Wu //
3dd46a080SHaojian Wu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4dd46a080SHaojian Wu // See https://llvm.org/LICENSE.txt for license information.
5dd46a080SHaojian Wu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dd46a080SHaojian Wu //
7dd46a080SHaojian Wu //===----------------------------------------------------------------------===//
8dd46a080SHaojian Wu 
9dd46a080SHaojian Wu #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
10dd46a080SHaojian Wu #include "clang/Basic/SourceLocation.h"
11dd46a080SHaojian Wu #include "clang/Lex/HeaderSearch.h"
12dd46a080SHaojian Wu 
13dd46a080SHaojian Wu namespace clang::tooling {
14dd46a080SHaojian Wu namespace {
15dd46a080SHaojian Wu 
16dd46a080SHaojian Wu // Is Line an #if or #ifdef directive?
17dd46a080SHaojian Wu // FIXME: This makes headers with #ifdef LINUX/WINDOWS/MACOS marked as non
18dd46a080SHaojian Wu // self-contained and is probably not what we want.
isIf(llvm::StringRef Line)19dd46a080SHaojian Wu bool isIf(llvm::StringRef Line) {
20dd46a080SHaojian Wu   Line = Line.ltrim();
21dd46a080SHaojian Wu   if (!Line.consume_front("#"))
22dd46a080SHaojian Wu     return false;
23dd46a080SHaojian Wu   Line = Line.ltrim();
24*f3dcc235SKazu Hirata   return Line.starts_with("if");
25dd46a080SHaojian Wu }
26dd46a080SHaojian Wu 
27dd46a080SHaojian Wu // Is Line an #error directive mentioning includes?
isErrorAboutInclude(llvm::StringRef Line)28dd46a080SHaojian Wu bool isErrorAboutInclude(llvm::StringRef Line) {
29dd46a080SHaojian Wu   Line = Line.ltrim();
30dd46a080SHaojian Wu   if (!Line.consume_front("#"))
31dd46a080SHaojian Wu     return false;
32dd46a080SHaojian Wu   Line = Line.ltrim();
33*f3dcc235SKazu Hirata   if (!Line.starts_with("error"))
34dd46a080SHaojian Wu     return false;
35dd46a080SHaojian Wu   return Line.contains_insensitive(
36dd46a080SHaojian Wu       "includ"); // Matches "include" or "including".
37dd46a080SHaojian Wu }
38dd46a080SHaojian Wu 
39dd46a080SHaojian Wu // Heuristically headers that only want to be included via an umbrella.
isDontIncludeMeHeader(StringRef Content)4051f1ae52SDavid Goldman bool isDontIncludeMeHeader(StringRef Content) {
41dd46a080SHaojian Wu   llvm::StringRef Line;
42dd46a080SHaojian Wu   // Only sniff up to 100 lines or 10KB.
43dd46a080SHaojian Wu   Content = Content.take_front(100 * 100);
44dd46a080SHaojian Wu   for (unsigned I = 0; I < 100 && !Content.empty(); ++I) {
45dd46a080SHaojian Wu     std::tie(Line, Content) = Content.split('\n');
46dd46a080SHaojian Wu     if (isIf(Line) && isErrorAboutInclude(Content.split('\n').first))
47dd46a080SHaojian Wu       return true;
48dd46a080SHaojian Wu   }
49dd46a080SHaojian Wu   return false;
50dd46a080SHaojian Wu }
51dd46a080SHaojian Wu 
isImportLine(llvm::StringRef Line)5251f1ae52SDavid Goldman bool isImportLine(llvm::StringRef Line) {
5351f1ae52SDavid Goldman   Line = Line.ltrim();
5451f1ae52SDavid Goldman   if (!Line.consume_front("#"))
5551f1ae52SDavid Goldman     return false;
5651f1ae52SDavid Goldman   Line = Line.ltrim();
57*f3dcc235SKazu Hirata   return Line.starts_with("import");
5851f1ae52SDavid Goldman }
5951f1ae52SDavid Goldman 
getFileContents(FileEntryRef FE,const SourceManager & SM)603661a48aSJan Svoboda llvm::StringRef getFileContents(FileEntryRef FE, const SourceManager &SM) {
6151f1ae52SDavid Goldman   return const_cast<SourceManager &>(SM)
6251f1ae52SDavid Goldman       .getMemoryBufferForFileOrNone(FE)
6351f1ae52SDavid Goldman       .value_or(llvm::MemoryBufferRef())
6451f1ae52SDavid Goldman       .getBuffer();
6551f1ae52SDavid Goldman }
6651f1ae52SDavid Goldman 
67dd46a080SHaojian Wu } // namespace
68dd46a080SHaojian Wu 
isSelfContainedHeader(FileEntryRef FE,const SourceManager & SM,const HeaderSearch & HeaderInfo)69b0abc9ddSJan Svoboda bool isSelfContainedHeader(FileEntryRef FE, const SourceManager &SM,
703ddd1864SHaojian Wu                            const HeaderSearch &HeaderInfo) {
71dd46a080SHaojian Wu   if (!HeaderInfo.isFileMultipleIncludeGuarded(FE) &&
7251f1ae52SDavid Goldman       !HeaderInfo.hasFileBeenImported(FE) &&
7351f1ae52SDavid Goldman       // Any header that contains #imports is supposed to be #import'd so no
7451f1ae52SDavid Goldman       // need to check for anything but the main-file.
7551f1ae52SDavid Goldman       (SM.getFileEntryForID(SM.getMainFileID()) != FE ||
7651f1ae52SDavid Goldman        !codeContainsImports(getFileContents(FE, SM))))
77dd46a080SHaojian Wu     return false;
78dd46a080SHaojian Wu   // This pattern indicates that a header can't be used without
79dd46a080SHaojian Wu   // particular preprocessor state, usually set up by another header.
8051f1ae52SDavid Goldman   return !isDontIncludeMeHeader(getFileContents(FE, SM));
8151f1ae52SDavid Goldman }
8251f1ae52SDavid Goldman 
codeContainsImports(llvm::StringRef Code)8351f1ae52SDavid Goldman bool codeContainsImports(llvm::StringRef Code) {
8451f1ae52SDavid Goldman   // Only sniff up to 100 lines or 10KB.
8551f1ae52SDavid Goldman   Code = Code.take_front(100 * 100);
8651f1ae52SDavid Goldman   llvm::StringRef Line;
8751f1ae52SDavid Goldman   for (unsigned I = 0; I < 100 && !Code.empty(); ++I) {
8851f1ae52SDavid Goldman     std::tie(Line, Code) = Code.split('\n');
8951f1ae52SDavid Goldman     if (isImportLine(Line))
9051f1ae52SDavid Goldman       return true;
9151f1ae52SDavid Goldman   }
9251f1ae52SDavid Goldman   return false;
93dd46a080SHaojian Wu }
9499b5ec1fSSam McCall 
parseIWYUPragma(const char * Text)9558c9467fSHaojian Wu std::optional<StringRef> parseIWYUPragma(const char *Text) {
9699b5ec1fSSam McCall   // Skip the comment start, // or /*.
9799b5ec1fSSam McCall   if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*'))
985891420eSKazu Hirata     return std::nullopt;
9999b5ec1fSSam McCall   bool BlockComment = Text[1] == '*';
10099b5ec1fSSam McCall   Text += 2;
10199b5ec1fSSam McCall 
10299b5ec1fSSam McCall   // Per spec, direcitves are whitespace- and case-sensitive.
10399b5ec1fSSam McCall   constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
10499b5ec1fSSam McCall   if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
1055891420eSKazu Hirata     return std::nullopt;
10699b5ec1fSSam McCall   Text += IWYUPragma.size();
10799b5ec1fSSam McCall   const char *End = Text;
10899b5ec1fSSam McCall   while (*End != 0 && *End != '\n')
10999b5ec1fSSam McCall     ++End;
11099b5ec1fSSam McCall   StringRef Rest(Text, End - Text);
11199b5ec1fSSam McCall   // Strip off whitespace and comment markers to avoid confusion. This isn't
11299b5ec1fSSam McCall   // fully-compatible with IWYU, which splits into whitespace-delimited tokens.
11399b5ec1fSSam McCall   if (BlockComment)
11499b5ec1fSSam McCall     Rest.consume_back("*/");
11599b5ec1fSSam McCall   return Rest.trim();
11699b5ec1fSSam McCall }
11799b5ec1fSSam McCall 
118dd46a080SHaojian Wu } // namespace clang::tooling
119