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