forked from OSchip/llvm-project
[clang] Enable output of SARIF diagnostics
Enables Clang to emit diagnostics in SARIF format when `-fdiagnostics-format=sarif`. Adds a new DiagnosticConsumer named SARIFDiagnosticPrinter and a new DiagnosticRenderer named SARIFDiagnostic to constuct and emit a SARIF object containing the run's basic diagnostic info. Reviewed By: cjdb, denik, aaron.ballman Differential Revision: https://reviews.llvm.org/D131632
This commit is contained in:
parent
47166968db
commit
82e893c47c
|
@ -0,0 +1,76 @@
|
|||
//===--- SARIFDiagnostic.h - SARIF Diagnostic Formatting -----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a utility class that provides support for constructing a SARIF object
|
||||
// containing diagnostics.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H
|
||||
#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTIC_H
|
||||
|
||||
#include "clang/Basic/Sarif.h"
|
||||
#include "clang/Frontend/DiagnosticRenderer.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
class SARIFDiagnostic : public DiagnosticRenderer {
|
||||
public:
|
||||
SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
|
||||
DiagnosticOptions *DiagOpts, SarifDocumentWriter *Writer);
|
||||
|
||||
~SARIFDiagnostic() = default;
|
||||
|
||||
SARIFDiagnostic &operator=(const SARIFDiagnostic &&) = delete;
|
||||
SARIFDiagnostic(SARIFDiagnostic &&) = delete;
|
||||
SARIFDiagnostic &operator=(const SARIFDiagnostic &) = delete;
|
||||
SARIFDiagnostic(const SARIFDiagnostic &) = delete;
|
||||
|
||||
protected:
|
||||
void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
DiagnosticsEngine::Level Level, StringRef Message,
|
||||
ArrayRef<CharSourceRange> Ranges,
|
||||
DiagOrStoredDiag D) override;
|
||||
|
||||
void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
DiagnosticsEngine::Level Level,
|
||||
ArrayRef<CharSourceRange> Ranges) override;
|
||||
|
||||
void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
|
||||
SmallVectorImpl<CharSourceRange> &Ranges,
|
||||
ArrayRef<FixItHint> Hints) override {}
|
||||
|
||||
void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override;
|
||||
|
||||
void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName) override;
|
||||
|
||||
void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName) override;
|
||||
|
||||
private:
|
||||
raw_ostream &OS;
|
||||
|
||||
// Shared between SARIFDiagnosticPrinter and this renderer.
|
||||
SarifDocumentWriter *Writer;
|
||||
|
||||
SarifResult addLocationToResult(SarifResult Result, FullSourceLoc Loc,
|
||||
PresumedLoc PLoc,
|
||||
ArrayRef<CharSourceRange> Ranges,
|
||||
const Diagnostic &Diag);
|
||||
|
||||
SarifRule addDiagnosticLevelToRule(SarifRule Rule,
|
||||
DiagnosticsEngine::Level Level);
|
||||
|
||||
llvm::StringRef emitFilename(StringRef Filename, const SourceManager &SM);
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
//===-- SARIFDiagnosticPrinter.h - SARIF Diagnostic Client -------*- C++-*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a concrete diagnostic client, which prints the diagnostics to
|
||||
// standard error in SARIF format.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H
|
||||
#define LLVM_CLANG_FRONTEND_SARIFDIAGNOSTICPRINTER_H
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/Sarif.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <memory>
|
||||
|
||||
namespace clang {
|
||||
class DiagnosticOptions;
|
||||
class LangOptions;
|
||||
class SARIFDiagnostic;
|
||||
class SarifDocumentWriter;
|
||||
|
||||
class SARIFDiagnosticPrinter : public DiagnosticConsumer {
|
||||
public:
|
||||
SARIFDiagnosticPrinter(raw_ostream &OS, DiagnosticOptions *Diags);
|
||||
~SARIFDiagnosticPrinter() = default;
|
||||
|
||||
SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &&) = delete;
|
||||
SARIFDiagnosticPrinter(SARIFDiagnosticPrinter &&) = delete;
|
||||
SARIFDiagnosticPrinter &operator=(const SARIFDiagnosticPrinter &) = delete;
|
||||
SARIFDiagnosticPrinter(const SARIFDiagnosticPrinter &) = delete;
|
||||
|
||||
/// setPrefix - Set the diagnostic printer prefix string, which will be
|
||||
/// printed at the start of any diagnostics. If empty, no prefix string is
|
||||
/// used.
|
||||
void setPrefix(llvm::StringRef Value) { Prefix = Value; }
|
||||
|
||||
bool hasSarifWriter() const { return Writer != nullptr; }
|
||||
|
||||
SarifDocumentWriter &getSarifWriter() const {
|
||||
assert(Writer && "SarifWriter not set!");
|
||||
return *Writer;
|
||||
}
|
||||
|
||||
void setSarifWriter(std::unique_ptr<SarifDocumentWriter> SarifWriter) {
|
||||
Writer = std::move(SarifWriter);
|
||||
}
|
||||
|
||||
void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override;
|
||||
void EndSourceFile() override;
|
||||
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
||||
const Diagnostic &Info) override;
|
||||
|
||||
private:
|
||||
raw_ostream &OS;
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
|
||||
|
||||
/// Handle to the currently active SARIF diagnostic emitter.
|
||||
std::unique_ptr<SARIFDiagnostic> SARIFDiag;
|
||||
|
||||
/// A string to prefix to error messages.
|
||||
std::string Prefix;
|
||||
|
||||
std::unique_ptr<SarifDocumentWriter> Writer;
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif
|
|
@ -31,6 +31,8 @@ add_clang_library(clangFrontend
|
|||
MultiplexConsumer.cpp
|
||||
PrecompiledPreamble.cpp
|
||||
PrintPreprocessedOutput.cpp
|
||||
SARIFDiagnostic.cpp
|
||||
SARIFDiagnosticPrinter.cpp
|
||||
SerializedDiagnosticPrinter.cpp
|
||||
SerializedDiagnosticReader.cpp
|
||||
TestModuleFileExtension.cpp
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/LangStandard.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/FrontendPluginRegistry.h"
|
||||
#include "clang/Frontend/LogDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/SARIFDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/SerializedDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
|
@ -346,6 +348,8 @@ CompilerInstance::createDiagnostics(DiagnosticOptions *Opts,
|
|||
// implementing -verify.
|
||||
if (Client) {
|
||||
Diags->setClient(Client, ShouldOwnClient);
|
||||
} else if (Opts->getFormat() == DiagnosticOptions::SARIF) {
|
||||
Diags->setClient(new SARIFDiagnosticPrinter(llvm::errs(), Opts));
|
||||
} else
|
||||
Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts));
|
||||
|
||||
|
|
|
@ -11,13 +11,16 @@
|
|||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/DeclGroup.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/LangStandard.h"
|
||||
#include "clang/Basic/Sarif.h"
|
||||
#include "clang/Frontend/ASTUnit.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/FrontendPluginRegistry.h"
|
||||
#include "clang/Frontend/LayoutOverrideSource.h"
|
||||
#include "clang/Frontend/MultiplexConsumer.h"
|
||||
#include "clang/Frontend/SARIFDiagnosticPrinter.h"
|
||||
#include "clang/Frontend/Utils.h"
|
||||
#include "clang/Lex/HeaderSearch.h"
|
||||
#include "clang/Lex/LiteralSupport.h"
|
||||
|
@ -35,6 +38,7 @@
|
|||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Timer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
using namespace clang;
|
||||
|
||||
|
@ -717,8 +721,14 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (!CI.hasSourceManager())
|
||||
if (!CI.hasSourceManager()) {
|
||||
CI.createSourceManager(CI.getFileManager());
|
||||
if (CI.getDiagnosticOpts().getFormat() == DiagnosticOptions::SARIF) {
|
||||
static_cast<SARIFDiagnosticPrinter *>(&CI.getDiagnosticClient())
|
||||
->setSarifWriter(
|
||||
std::make_unique<SarifDocumentWriter>(CI.getSourceManager()));
|
||||
}
|
||||
}
|
||||
|
||||
// Set up embedding for any specified files. Do this before we load any
|
||||
// source files, including the primary module map for the compilation.
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
//===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Frontend/SARIFDiagnostic.h"
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Basic/Sarif.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/Locale.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
|
||||
SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
|
||||
DiagnosticOptions *DiagOpts,
|
||||
SarifDocumentWriter *Writer)
|
||||
: DiagnosticRenderer(LangOpts, DiagOpts), OS(OS), Writer(Writer) {}
|
||||
|
||||
// FIXME(llvm-project/issues/57323): Refactor Diagnostic classes.
|
||||
void SARIFDiagnostic::emitDiagnosticMessage(
|
||||
FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
|
||||
StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
|
||||
DiagOrStoredDiag D) {
|
||||
|
||||
const auto *Diag = D.dyn_cast<const Diagnostic *>();
|
||||
|
||||
if (!Diag)
|
||||
return;
|
||||
|
||||
SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID()));
|
||||
|
||||
Rule = addDiagnosticLevelToRule(Rule, Level);
|
||||
|
||||
unsigned RuleIdx = Writer->createRule(Rule);
|
||||
|
||||
SarifResult Result =
|
||||
SarifResult::create(RuleIdx).setDiagnosticMessage(Message);
|
||||
|
||||
if (Loc.isValid())
|
||||
Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag);
|
||||
|
||||
Writer->appendResult(Result);
|
||||
}
|
||||
|
||||
SarifResult SARIFDiagnostic::addLocationToResult(
|
||||
SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) {
|
||||
SmallVector<CharSourceRange> Locations = {};
|
||||
|
||||
if (PLoc.isInvalid()) {
|
||||
// At least add the file name if available:
|
||||
FileID FID = Loc.getFileID();
|
||||
if (FID.isValid()) {
|
||||
if (const FileEntry *FE = Loc.getFileEntry()) {
|
||||
llvm::StringRef Filename =
|
||||
emitFilename(FE->getName(), Loc.getManager());
|
||||
// FIXME(llvm-project/issues/57366): File-only locations
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
FileID CaretFileID = Loc.getExpansionLoc().getFileID();
|
||||
|
||||
for (const CharSourceRange Range : Ranges) {
|
||||
// Ignore invalid ranges.
|
||||
if (Range.isInvalid())
|
||||
continue;
|
||||
|
||||
auto &SM = Loc.getManager();
|
||||
SourceLocation B = SM.getExpansionLoc(Range.getBegin());
|
||||
CharSourceRange ERange = SM.getExpansionRange(Range.getEnd());
|
||||
SourceLocation E = ERange.getEnd();
|
||||
bool IsTokenRange = ERange.isTokenRange();
|
||||
|
||||
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
|
||||
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
|
||||
|
||||
// If the start or end of the range is in another file, just discard
|
||||
// it.
|
||||
if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
|
||||
continue;
|
||||
|
||||
// Add in the length of the token, so that we cover multi-char
|
||||
// tokens.
|
||||
unsigned TokSize = 0;
|
||||
if (IsTokenRange)
|
||||
TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
|
||||
|
||||
FullSourceLoc BF(B, SM), EF(E, SM);
|
||||
SourceLocation BeginLoc = SM.translateLineCol(
|
||||
BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber());
|
||||
SourceLocation EndLoc = SM.translateLineCol(
|
||||
EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize);
|
||||
|
||||
Locations.push_back(
|
||||
CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false});
|
||||
// FIXME: Additional ranges should use presumed location in both
|
||||
// Text and SARIF diagnostics.
|
||||
}
|
||||
|
||||
auto &SM = Loc.getManager();
|
||||
auto FID = PLoc.getFileID();
|
||||
// Visual Studio 2010 or earlier expects column number to be off by one.
|
||||
unsigned int ColNo = (LangOpts.MSCompatibilityVersion &&
|
||||
!LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
|
||||
? PLoc.getColumn() - 1
|
||||
: PLoc.getColumn();
|
||||
SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo);
|
||||
|
||||
// FIXME(llvm-project/issues/57366): Properly process #line directives.
|
||||
Locations.push_back(
|
||||
CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false});
|
||||
|
||||
return Result.setLocations(Locations);
|
||||
}
|
||||
|
||||
SarifRule
|
||||
SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule,
|
||||
DiagnosticsEngine::Level Level) {
|
||||
auto Config = SarifReportingConfiguration::create();
|
||||
|
||||
switch (Level) {
|
||||
case DiagnosticsEngine::Note:
|
||||
Config = Config.setLevel(SarifResultLevel::Note);
|
||||
break;
|
||||
case DiagnosticsEngine::Remark:
|
||||
Config = Config.setLevel(SarifResultLevel::None);
|
||||
break;
|
||||
case DiagnosticsEngine::Warning:
|
||||
Config = Config.setLevel(SarifResultLevel::Warning);
|
||||
break;
|
||||
case DiagnosticsEngine::Error:
|
||||
Config = Config.setLevel(SarifResultLevel::Error).setRank(50);
|
||||
break;
|
||||
case DiagnosticsEngine::Fatal:
|
||||
Config = Config.setLevel(SarifResultLevel::Error).setRank(100);
|
||||
break;
|
||||
case DiagnosticsEngine::Ignored:
|
||||
assert(false && "Invalid diagnostic type");
|
||||
}
|
||||
|
||||
return Rule.setDefaultConfiguration(Config);
|
||||
}
|
||||
|
||||
llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename,
|
||||
const SourceManager &SM) {
|
||||
if (DiagOpts->AbsolutePath) {
|
||||
llvm::ErrorOr<const FileEntry *> File =
|
||||
SM.getFileManager().getFile(Filename);
|
||||
if (File) {
|
||||
// We want to print a simplified absolute path, i. e. without "dots".
|
||||
//
|
||||
// The hardest part here are the paths like "<part1>/<link>/../<part2>".
|
||||
// On Unix-like systems, we cannot just collapse "<link>/..", because
|
||||
// paths are resolved sequentially, and, thereby, the path
|
||||
// "<part1>/<part2>" may point to a different location. That is why
|
||||
// we use FileManager::getCanonicalName(), which expands all indirections
|
||||
// with llvm::sys::fs::real_path() and caches the result.
|
||||
//
|
||||
// On the other hand, it would be better to preserve as much of the
|
||||
// original path as possible, because that helps a user to recognize it.
|
||||
// real_path() expands all links, which is sometimes too much. Luckily,
|
||||
// on Windows we can just use llvm::sys::path::remove_dots(), because,
|
||||
// on that system, both aforementioned paths point to the same place.
|
||||
#ifdef _WIN32
|
||||
SmallString<256> TmpFilename = (*File)->getName();
|
||||
llvm::sys::fs::make_absolute(TmpFilename);
|
||||
llvm::sys::path::native(TmpFilename);
|
||||
llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
|
||||
Filename = StringRef(TmpFilename.data(), TmpFilename.size());
|
||||
#else
|
||||
Filename = SM.getFileManager().getCanonicalName(*File);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return Filename;
|
||||
}
|
||||
|
||||
/// Print out the file/line/column information and include trace.
|
||||
///
|
||||
/// This method handlen the emission of the diagnostic location information.
|
||||
/// This includes extracting as much location information as is present for
|
||||
/// the diagnostic and printing it, as well as any include stack or source
|
||||
/// ranges necessary.
|
||||
void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
DiagnosticsEngine::Level Level,
|
||||
ArrayRef<CharSourceRange> Ranges) {
|
||||
assert(false && "Not implemented in SARIF mode");
|
||||
}
|
||||
|
||||
void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
|
||||
assert(false && "Not implemented in SARIF mode");
|
||||
}
|
||||
|
||||
void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
|
||||
StringRef ModuleName) {
|
||||
assert(false && "Not implemented in SARIF mode");
|
||||
}
|
||||
|
||||
void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc,
|
||||
PresumedLoc PLoc,
|
||||
StringRef ModuleName) {
|
||||
assert(false && "Not implemented in SARIF mode");
|
||||
}
|
||||
} // namespace clang
|
|
@ -0,0 +1,83 @@
|
|||
//===------- SARIFDiagnosticPrinter.cpp - Diagnostic Printer---------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This diagnostic client prints out their diagnostic messages in SARIF format.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Frontend/SARIFDiagnosticPrinter.h"
|
||||
#include "clang/Basic/DiagnosticOptions.h"
|
||||
#include "clang/Basic/Sarif.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/DiagnosticRenderer.h"
|
||||
#include "clang/Frontend/SARIFDiagnostic.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/JSON.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace clang {
|
||||
|
||||
SARIFDiagnosticPrinter::SARIFDiagnosticPrinter(raw_ostream &OS,
|
||||
DiagnosticOptions *Diags)
|
||||
: OS(OS), DiagOpts(Diags) {}
|
||||
|
||||
void SARIFDiagnosticPrinter::BeginSourceFile(const LangOptions &LO,
|
||||
const Preprocessor *PP) {
|
||||
// Build the SARIFDiagnostic utility.
|
||||
assert(hasSarifWriter() && "Writer not set!");
|
||||
assert(!SARIFDiag && "SARIFDiagnostic already set.");
|
||||
SARIFDiag = std::make_unique<SARIFDiagnostic>(OS, LO, &*DiagOpts, &*Writer);
|
||||
// Initialize the SARIF object.
|
||||
Writer->createRun("clang", Prefix);
|
||||
}
|
||||
|
||||
void SARIFDiagnosticPrinter::EndSourceFile() {
|
||||
assert(SARIFDiag && "SARIFDiagnostic has not been set.");
|
||||
Writer->endRun();
|
||||
llvm::json::Value Value(Writer->createDocument());
|
||||
OS << "\n" << Value << "\n\n";
|
||||
OS.flush();
|
||||
SARIFDiag.reset();
|
||||
}
|
||||
|
||||
void SARIFDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level,
|
||||
const Diagnostic &Info) {
|
||||
assert(SARIFDiag && "SARIFDiagnostic has not been set.");
|
||||
// Default implementation (Warnings/errors count). Keeps track of the
|
||||
// number of errors.
|
||||
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
||||
|
||||
// Render the diagnostic message into a temporary buffer eagerly. We'll use
|
||||
// this later as we add the diagnostic to the SARIF object.
|
||||
SmallString<100> OutStr;
|
||||
Info.FormatDiagnostic(OutStr);
|
||||
|
||||
llvm::raw_svector_ostream DiagMessageStream(OutStr);
|
||||
|
||||
// Use a dedicated, simpler path for diagnostics without a valid location.
|
||||
// This is important as if the location is missing, we may be emitting
|
||||
// diagnostics in a context that lacks language options, a source manager, or
|
||||
// other infrastructure necessary when emitting more rich diagnostics.
|
||||
if (Info.getLocation().isInvalid()) {
|
||||
// FIXME: Enable diagnostics without a source manager
|
||||
return;
|
||||
}
|
||||
|
||||
// Assert that the rest of our infrastructure is setup properly.
|
||||
assert(DiagOpts && "Unexpected diagnostic without options set");
|
||||
assert(Info.hasSourceManager() &&
|
||||
"Unexpected diagnostic with no source manager");
|
||||
|
||||
SARIFDiag->emitDiagnostic(
|
||||
FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level,
|
||||
DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints(), &Info);
|
||||
}
|
||||
} // namespace clang
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: %clang -fsyntax-only -Wall -Wextra -fdiagnostics-format=sarif %s > %t 2>&1 || true
|
||||
// RUN: FileCheck -dump-input=always %s --input-file=%t
|
||||
// CHECK: warning: diagnostic formatting in SARIF mode is currently unstable [-Wsarif-format-unstable]
|
||||
// CHECK: {"$schema":"https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json","runs":[{"artifacts":[{"length":
|
||||
// Omit exact length of this file
|
||||
// CHECK: ,"location":{"index":0,"uri":"file://
|
||||
// Omit filepath to llvm project directory
|
||||
// CHECK: clang/test/Frontend/sarif-diagnostics.cpp"},"mimeType":"text/plain","roles":["resultFile"]}],"columnKind":"unicodeCodePoints","results":[{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":12}}}],"message":{"text":"'main' must return 'int'"},"ruleId":"3465","ruleIndex":0},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":11,"startColumn":11,"startLine":13}}}],"message":{"text":"use of undeclared identifier 'hello'"},"ruleId":"4604","ruleIndex":1},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":17,"startColumn":17,"startLine":15}}}],"message":{"text":"invalid digit 'a' in decimal constant"},"ruleId":"898","ruleIndex":2},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":5,"startColumn":5,"startLine":19}}}],"message":{"text":"misleading indentation; statement is not part of the previous 'if'"},"ruleId":"1806","ruleIndex":3},{"level":"note","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":3,"startColumn":3,"startLine":17}}}],"message":{"text":"previous statement is here"},"ruleId":"1730","ruleIndex":4},{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":10,"startColumn":10,"startLine":18}}}],"message":{"text":"unused variable 'Yes'"},"ruleId":"6539","ruleIndex":5},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":12,"startColumn":12,"startLine":21}}}],"message":{"text":"use of undeclared identifier 'hi'"},"ruleId":"4604","ruleIndex":6},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":1,"startColumn":1,"startLine":23}}}],"message":{"text":"extraneous closing brace ('}')"},"ruleId":"1399","ruleIndex":7},{"level":"error","locations":[{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":6,"endLine":27,"startColumn":5,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":10,"endLine":27,"startColumn":9,"startLine":27}}},{"physicalLocation":{"artifactLocation":{"index":0},"region":{"endColumn":7,"startColumn":7,"startLine":27}}}],"message":{"text":"invalid operands to binary expression ('t1' and 't1')"},"ruleId":"4539","ruleIndex":8}],"tool":{"driver":{"fullName":"","informationUri":"https://clang.llvm.org/docs/UsersManual.html","language":"en-US","name":"clang","rules":[{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"3465","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4604","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"898","name":""},{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"1806","name":""},{"defaultConfiguration":{"enabled":true,"level":"note","rank":-1},"fullDescription":{"text":""},"id":"1730","name":""},{"defaultConfiguration":{"enabled":true,"level":"warning","rank":-1},"fullDescription":{"text":""},"id":"6539","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4604","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"1399","name":""},{"defaultConfiguration":{"enabled":true,"level":"error","rank":50},"fullDescription":{"text":""},"id":"4539","name":""}],"version":"16.0.0"}}}],"version":"2.1.0"}
|
||||
// CHECK: 2 warnings and 6 errors generated.
|
||||
|
||||
|
||||
void main() {
|
||||
int i = hello;
|
||||
|
||||
float test = 1a.0;
|
||||
|
||||
if (true)
|
||||
bool Yes = true;
|
||||
return;
|
||||
|
||||
bool j = hi;
|
||||
}
|
||||
}
|
||||
|
||||
struct t1 { };
|
||||
void f1(t1 x, t1 y) {
|
||||
x + y;
|
||||
}
|
Loading…
Reference in New Issue