Ejemplo n.º 1
0
/// \brief Print any diagnostic option information to a raw_ostream.
///
/// This implements all of the logic for adding diagnostic options to a message
/// (via OS). Each relevant option is comma separated and all are enclosed in
/// the standard bracketing: " [...]".
static void printDiagnosticOptions(raw_ostream &OS,
                                   DiagnosticsEngine::Level Level,
                                   const Diagnostic &Info,
                                   const DiagnosticOptions &DiagOpts) {
  bool Started = false;
  if (DiagOpts.ShowOptionNames) {
    // Handle special cases for non-warnings early.
    if (Info.getID() == diag::fatal_too_many_errors) {
      OS << " [-ferror-limit=]";
      return;
    }

    // The code below is somewhat fragile because we are essentially trying to
    // report to the user what happened by inferring what the diagnostic engine
    // did. Eventually it might make more sense to have the diagnostic engine
    // include some "why" information in the diagnostic.

    // If this is a warning which has been mapped to an error by the user (as
    // inferred by checking whether the default mapping is to an error) then
    // flag it as such. Note that diagnostics could also have been mapped by a
    // pragma, but we don't currently have a way to distinguish this.
    if (Level == DiagnosticsEngine::Error &&
        DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) &&
        !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) {
      OS << " [-Werror";
      Started = true;
    }

    StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
    if (!Opt.empty()) {
      OS << (Started ? "," : " [")
         << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
      StringRef OptValue = Info.getDiags()->getFlagValue();
      if (!OptValue.empty())
        OS << "=" << OptValue;
      Started = true;
    }
  }

  // If the user wants to see category information, include it too.
  if (DiagOpts.ShowCategories) {
    unsigned DiagCategory =
      DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
    if (DiagCategory) {
      OS << (Started ? "," : " [");
      Started = true;
      if (DiagOpts.ShowCategories == 1)
        OS << DiagCategory;
      else {
        assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value");
        OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory);
      }
    }
  }
  if (Started)
    OS << ']';
}
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
  if (DiagLevel == DiagnosticsEngine::Note) {
    assert(!Errors.empty() &&
           "A diagnostic note can only be appended to a message.");
  } else {
    finalizeLastError();
    StringRef WarningOption =
        Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
            Info.getID());
    std::string CheckName = !WarningOption.empty()
                                ? ("clang-diagnostic-" + WarningOption).str()
                                : Context.getCheckName(Info.getID()).str();

    if (CheckName.empty()) {
      // This is a compiler diagnostic without a warning option. Assign check
      // name based on its level.
      switch (DiagLevel) {
        case DiagnosticsEngine::Error:
        case DiagnosticsEngine::Fatal:
          CheckName = "clang-diagnostic-error";
          break;
        case DiagnosticsEngine::Warning:
          CheckName = "clang-diagnostic-warning";
          break;
        default:
          CheckName = "clang-diagnostic-unknown";
          break;
      }
    }

    ClangTidyError::Level Level = ClangTidyError::Warning;
    if (DiagLevel == DiagnosticsEngine::Error ||
        DiagLevel == DiagnosticsEngine::Fatal) {
      // Force reporting of Clang errors regardless of filters and non-user
      // code.
      Level = ClangTidyError::Error;
      LastErrorRelatesToUserCode = true;
      LastErrorPassesLineFilter = true;
    }
    Errors.push_back(ClangTidyError(CheckName, Level));
  }

  // FIXME: Provide correct LangOptions for each file.
  LangOptions LangOpts;
  ClangTidyDiagnosticRenderer Converter(
      LangOpts, &Context.DiagEngine->getDiagnosticOptions(), Errors.back());
  SmallString<100> Message;
  Info.FormatDiagnostic(Message);
  SourceManager *Sources = nullptr;
  if (Info.hasSourceManager())
    Sources = &Info.getSourceManager();
  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
                           Info.getRanges(), Info.getFixItHints(), Sources);

  checkFilters(Info.getLocation());
}
unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel,
                                             const Diagnostic &Info) {
  if (DiagLevel == DiagnosticsEngine::Note)
    return 0; // No flag for notes.
  
  StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(Info.getID());
  if (FlagName.empty())
    return 0;

  // Here we assume that FlagName points to static data whose pointer
  // value is fixed.  This allows us to unique by diagnostic groups.
  const void *data = FlagName.data();
  std::pair<unsigned, StringRef> &entry = DiagFlags[data];
  if (entry.first == 0) {
    entry.first = DiagFlags.size();
    entry.second = FlagName;
    
    // Lazily emit the string in a separate record.
    RecordData Record;
    Record.push_back(RECORD_DIAG_FLAG);
    Record.push_back(entry.first);
    Record.push_back(FlagName.size());
    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG_FLAG),
                              Record, FlagName);    
  }

  return entry.first;
}
Ejemplo n.º 4
0
 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                               const Diagnostic &Info) override {
   if (Info.getID() == clang::diag::warn_drv_input_file_unused) {
     // Arg 1 for this diagnostic is the option that didn't get used.
     UnusedInputs.push_back(Info.getArgStdStr(0));
   }
   if (Other)
     Other->HandleDiagnostic(DiagLevel, Info);
 }
void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                                    const Diagnostic &Info) {

  if (DiagLevel != DiagnosticsEngine::Note) {
    if (inNonNoteDiagnostic) {
      // We have encountered a non-note diagnostic.  Finish up the previous
      // diagnostic block before starting a new one.
      Stream.ExitBlock();
    }
    inNonNoteDiagnostic = true;
  }
  
  Stream.EnterSubblock(BLOCK_DIAG, 4);
  
  // Emit the RECORD_DIAG record.
  Record.clear();
  Record.push_back(RECORD_DIAG);
  Record.push_back(DiagLevel);
  AddLocToRecord(Info.getLocation(), Record);    
  // Emit the category string lazily and get the category ID.
  Record.push_back(getEmitCategory(Info.getID()));
  // Emit the diagnostic flag string lazily and get the mapped ID.
  Record.push_back(getEmitDiagnosticFlag(DiagLevel, Info));
  // Emit the diagnostic text.
  diagBuf.clear();   
  Info.FormatDiagnostic(diagBuf); // Compute the diagnostic text.
  Record.push_back(diagBuf.str().size());
  Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, diagBuf.str());

  // Emit Source Ranges.
  ArrayRef<CharSourceRange> Ranges = Info.getRanges();
  for (ArrayRef<CharSourceRange>::iterator it=Ranges.begin(), ei=Ranges.end();
       it != ei; ++it) {
    EmitCharSourceRange(*it);    
  }

  // Emit FixIts.
  for (unsigned i = 0, n = Info.getNumFixItHints(); i != n; ++i) {
    const FixItHint &fix = Info.getFixItHint(i);
    if (fix.isNull())
      continue;
    Record.clear();
    Record.push_back(RECORD_FIXIT);
    AddCharSourceRangeToRecord(fix.RemoveRange, Record);
    Record.push_back(fix.CodeToInsert.size());
    Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record,
                              fix.CodeToInsert);    
  }
  
  if (DiagLevel == DiagnosticsEngine::Note) {
    // Notes currently cannot have child diagnostics.  Complete the
    // diagnostic now.
    Stream.ExitBlock();
  }
}
Ejemplo n.º 6
0
void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
                                            const Diagnostic &Info) {
  // Default implementation (Warnings/errors count).
  DiagnosticConsumer::HandleDiagnostic(Level, Info);

  // Initialize the main file name, if we haven't already fetched it.
  if (MainFilename.empty() && Info.hasSourceManager()) {
    const SourceManager &SM = Info.getSourceManager();
    FileID FID = SM.getMainFileID();
    if (!FID.isInvalid()) {
      const FileEntry *FE = SM.getFileEntryForID(FID);
      if (FE && FE->getName())
        MainFilename = FE->getName();
    }
  }

  // Create the diag entry.
  DiagEntry DE;
  DE.DiagnosticID = Info.getID();
  DE.DiagnosticLevel = Level;

  // Format the message.
  SmallString<100> MessageStr;
  Info.FormatDiagnostic(MessageStr);
  DE.Message = MessageStr.str();

  // Set the location information.
  DE.Filename = "";
  DE.Line = DE.Column = 0;
  if (Info.getLocation().isValid() && Info.hasSourceManager()) {
    const SourceManager &SM = Info.getSourceManager();
    PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation());

    if (PLoc.isInvalid()) {
      // At least print the file name if available:
      FileID FID = SM.getFileID(Info.getLocation());
      if (!FID.isInvalid()) {
        const FileEntry *FE = SM.getFileEntryForID(FID);
        if (FE && FE->getName())
          DE.Filename = FE->getName();
      }
    } else {
      DE.Filename = PLoc.getFilename();
      DE.Line = PLoc.getLine();
      DE.Column = PLoc.getColumn();
    }
  }

  // Record the diagnostic entry.
  Entries.push_back(DE);
}
Ejemplo n.º 7
0
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level,
                                   const Diagnostic &Info)
  : ID(Info.getID()), Level(Level)
{
  assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) &&
       "Valid source location without setting a source manager for diagnostic");
  if (Info.getLocation().isValid())
    Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
  SmallString<64> Message;
  Info.FormatDiagnostic(Message);
  this->Message.assign(Message.begin(), Message.end());
  this->Ranges.assign(Info.getRanges().begin(), Info.getRanges().end());
  this->FixIts.assign(Info.getFixItHints().begin(), Info.getFixItHints().end());
}
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
  // FIXME: Demultiplex diagnostics.
  // FIXME: Ensure that we don't get notes from user code related to errors
  // from non-user code.
  if (Diags->getSourceManager().isInSystemHeader(Info.getLocation()))
    return;
  if (DiagLevel != DiagnosticsEngine::Note) {
    Errors.push_back(
        ClangTidyError(Context.getCheckName(Info.getID()), getMessage(Info)));
  } else {
    assert(!Errors.empty() &&
           "A diagnostic note can only be appended to a message.");
    Errors.back().Notes.push_back(getMessage(Info));
  }
  addFixes(Info, Errors.back());
}
Ejemplo n.º 9
0
StoredDiagnostic::StoredDiagnostic(DiagnosticsEngine::Level Level,
                                   const Diagnostic &Info)
    : ID(Info.getID()), Level(Level)
{
    assert((Info.getLocation().isInvalid() || Info.hasSourceManager()) &&
           "Valid source location without setting a source manager for diagnostic");
    if (Info.getLocation().isValid())
        Loc = FullSourceLoc(Info.getLocation(), Info.getSourceManager());
    SmallString<64> Message;
    Info.FormatDiagnostic(Message);
    this->Message.assign(Message.begin(), Message.end());

    Ranges.reserve(Info.getNumRanges());
    for (unsigned I = 0, N = Info.getNumRanges(); I != N; ++I)
        Ranges.push_back(Info.getRange(I));

    FixIts.reserve(Info.getNumFixItHints());
    for (unsigned I = 0, N = Info.getNumFixItHints(); I != N; ++I)
        FixIts.push_back(Info.getFixItHint(I));
}
Ejemplo n.º 10
0
void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
  auto behavior = state.determineBehavior(diagnostic.getID());
  if (behavior == DiagnosticState::Behavior::Ignore)
    return;

  // Figure out the source location.
  SourceLoc loc = diagnostic.getLoc();
  if (loc.isInvalid() && diagnostic.getDecl()) {
    const Decl *decl = diagnostic.getDecl();
    // If a declaration was provided instead of a location, and that declaration
    // has a location we can point to, use that location.
    loc = decl->getLoc();

    if (loc.isInvalid()) {
      // There is no location we can point to. Pretty-print the declaration
      // so we can point to it.
      SourceLoc ppLoc = PrettyPrintedDeclarations[decl];
      if (ppLoc.isInvalid()) {
        class TrackingPrinter : public StreamPrinter {
          SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries;

        public:
          TrackingPrinter(
              SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries,
              raw_ostream &OS) :
            StreamPrinter(OS), Entries(Entries) {}

          void printDeclLoc(const Decl *D) override {
            Entries.push_back({ D, OS.tell() });
          }
        };
        SmallVector<std::pair<const Decl *, uint64_t>, 8> entries;
        llvm::SmallString<128> buffer;
        llvm::SmallString<128> bufferName;
        {
          // Figure out which declaration to print. It's the top-most
          // declaration (not a module).
          const Decl *ppDecl = decl;
          auto dc = decl->getDeclContext();

          // FIXME: Horrible, horrible hackaround. We're not getting a
          // DeclContext everywhere we should.
          if (!dc) {
            return;
          }

          while (!dc->isModuleContext()) {
            switch (dc->getContextKind()) {
            case DeclContextKind::Module:
              llvm_unreachable("Not in a module context!");
              break;

            case DeclContextKind::FileUnit:
            case DeclContextKind::TopLevelCodeDecl:
              break;

            case DeclContextKind::ExtensionDecl:
              ppDecl = cast<ExtensionDecl>(dc);
              break;

            case DeclContextKind::GenericTypeDecl:
              ppDecl = cast<GenericTypeDecl>(dc);
              break;

            case DeclContextKind::SerializedLocal:
            case DeclContextKind::Initializer:
            case DeclContextKind::AbstractClosureExpr:
            case DeclContextKind::AbstractFunctionDecl:
            case DeclContextKind::SubscriptDecl:
              break;
            }

            dc = dc->getParent();
          }

          // Build the module name path (in reverse), which we use to
          // build the name of the buffer.
          SmallVector<StringRef, 4> nameComponents;
          while (dc) {
            nameComponents.push_back(cast<Module>(dc)->getName().str());
            dc = dc->getParent();
          }

          for (unsigned i = nameComponents.size(); i; --i) {
            bufferName += nameComponents[i-1];
            bufferName += '.';
          }

          if (auto value = dyn_cast<ValueDecl>(ppDecl)) {
            bufferName += value->getNameStr();
          } else if (auto ext = dyn_cast<ExtensionDecl>(ppDecl)) {
            bufferName += ext->getExtendedType().getString();
          }

          // Pretty-print the declaration we've picked.
          llvm::raw_svector_ostream out(buffer);
          TrackingPrinter printer(entries, out);
          ppDecl->print(printer, PrintOptions::printForDiagnostics());
        }

        // Build a buffer with the pretty-printed declaration.
        auto bufferID = SourceMgr.addMemBufferCopy(buffer, bufferName);
        auto memBufferStartLoc = SourceMgr.getLocForBufferStart(bufferID);

        // Go through all of the pretty-printed entries and record their
        // locations.
        for (auto entry : entries) {
          PrettyPrintedDeclarations[entry.first] =
              memBufferStartLoc.getAdvancedLoc(entry.second);
        }

        // Grab the pretty-printed location.
        ppLoc = PrettyPrintedDeclarations[decl];
      }

      loc = ppLoc;
    }
  }

  // Actually substitute the diagnostic arguments into the diagnostic text.
  llvm::SmallString<256> Text;
  {
    llvm::raw_svector_ostream Out(Text);
    formatDiagnosticText(diagnosticStrings[(unsigned)diagnostic.getID()],
                         diagnostic.getArgs(), Out);
  }

  // Pass the diagnostic off to the consumer.
  DiagnosticInfo Info;
  Info.ID = diagnostic.getID();
  Info.Ranges = diagnostic.getRanges();
  Info.FixIts = diagnostic.getFixIts();
  for (auto &Consumer : Consumers) {
    Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior), Text,
                               Info);
  }
}
/// \brief Print the diagnostic name to a raw_ostream.
///
/// This prints the diagnostic name to a raw_ostream if it has one. It formats
/// the name according to the expected diagnostic message formatting:
///   " [diagnostic_name_here]"
static void printDiagnosticName(raw_ostream &OS, const Diagnostic &Info) {
  if (!DiagnosticIDs::isBuiltinNote(Info.getID()))
    OS << " [" << DiagnosticIDs::getName(Info.getID()) << "]";
}
void ClangTidyDiagnosticConsumer::HandleDiagnostic(
    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
    return;

  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
      DiagLevel != DiagnosticsEngine::Fatal &&
      LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
                                    Info.getLocation())) {
    ++Context.Stats.ErrorsIgnoredNOLINT;
    // Ignored a warning, should ignore related notes as well
    LastErrorWasIgnored = true;
    return;
  }

  LastErrorWasIgnored = false;
  // Count warnings/errors.
  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);

  if (DiagLevel == DiagnosticsEngine::Note) {
    assert(!Errors.empty() &&
           "A diagnostic note can only be appended to a message.");
  } else {
    finalizeLastError();
    StringRef WarningOption =
        Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
            Info.getID());
    std::string CheckName = !WarningOption.empty()
                                ? ("clang-diagnostic-" + WarningOption).str()
                                : Context.getCheckName(Info.getID()).str();

    if (CheckName.empty()) {
      // This is a compiler diagnostic without a warning option. Assign check
      // name based on its level.
      switch (DiagLevel) {
      case DiagnosticsEngine::Error:
      case DiagnosticsEngine::Fatal:
        CheckName = "clang-diagnostic-error";
        break;
      case DiagnosticsEngine::Warning:
        CheckName = "clang-diagnostic-warning";
        break;
      default:
        CheckName = "clang-diagnostic-unknown";
        break;
      }
    }

    ClangTidyError::Level Level = ClangTidyError::Warning;
    if (DiagLevel == DiagnosticsEngine::Error ||
        DiagLevel == DiagnosticsEngine::Fatal) {
      // Force reporting of Clang errors regardless of filters and non-user
      // code.
      Level = ClangTidyError::Error;
      LastErrorRelatesToUserCode = true;
      LastErrorPassesLineFilter = true;
    }
    bool IsWarningAsError =
        DiagLevel == DiagnosticsEngine::Warning &&
        Context.getWarningAsErrorFilter().contains(CheckName);
    Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
                        IsWarningAsError);
  }

  ClangTidyDiagnosticRenderer Converter(
      Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
      Errors.back());
  SmallString<100> Message;
  Info.FormatDiagnostic(Message);
  SourceManager *Sources = nullptr;
  if (Info.hasSourceManager())
    Sources = &Info.getSourceManager();
  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
                           Info.getRanges(), Info.getFixItHints(), Sources);

  checkFilters(Info.getLocation());
}