brintos

brintos / llvm-project-archived public Read only

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