182e893c4SAbraham Corea Diaz //===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===//
282e893c4SAbraham Corea Diaz //
382e893c4SAbraham Corea Diaz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
482e893c4SAbraham Corea Diaz // See https://llvm.org/LICENSE.txt for license information.
582e893c4SAbraham Corea Diaz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
682e893c4SAbraham Corea Diaz //
782e893c4SAbraham Corea Diaz //===----------------------------------------------------------------------===//
882e893c4SAbraham Corea Diaz
982e893c4SAbraham Corea Diaz #include "clang/Frontend/SARIFDiagnostic.h"
1082e893c4SAbraham Corea Diaz #include "clang/Basic/CharInfo.h"
1182e893c4SAbraham Corea Diaz #include "clang/Basic/DiagnosticOptions.h"
1282e893c4SAbraham Corea Diaz #include "clang/Basic/FileManager.h"
1382e893c4SAbraham Corea Diaz #include "clang/Basic/Sarif.h"
1482e893c4SAbraham Corea Diaz #include "clang/Basic/SourceLocation.h"
1582e893c4SAbraham Corea Diaz #include "clang/Basic/SourceManager.h"
1682e893c4SAbraham Corea Diaz #include "clang/Lex/Lexer.h"
1782e893c4SAbraham Corea Diaz #include "llvm/ADT/ArrayRef.h"
1882e893c4SAbraham Corea Diaz #include "llvm/ADT/SmallString.h"
1982e893c4SAbraham Corea Diaz #include "llvm/ADT/StringExtras.h"
2082e893c4SAbraham Corea Diaz #include "llvm/ADT/StringRef.h"
2182e893c4SAbraham Corea Diaz #include "llvm/Support/Casting.h"
2282e893c4SAbraham Corea Diaz #include "llvm/Support/ConvertUTF.h"
2382e893c4SAbraham Corea Diaz #include "llvm/Support/ErrorHandling.h"
2482e893c4SAbraham Corea Diaz #include "llvm/Support/ErrorOr.h"
2582e893c4SAbraham Corea Diaz #include "llvm/Support/Locale.h"
2682e893c4SAbraham Corea Diaz #include "llvm/Support/Path.h"
2782e893c4SAbraham Corea Diaz #include "llvm/Support/raw_ostream.h"
2882e893c4SAbraham Corea Diaz #include <algorithm>
2982e893c4SAbraham Corea Diaz #include <string>
3082e893c4SAbraham Corea Diaz
3182e893c4SAbraham Corea Diaz namespace clang {
3282e893c4SAbraham Corea Diaz
SARIFDiagnostic(raw_ostream & OS,const LangOptions & LangOpts,DiagnosticOptions * DiagOpts,SarifDocumentWriter * Writer)3382e893c4SAbraham Corea Diaz SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts,
3482e893c4SAbraham Corea Diaz DiagnosticOptions *DiagOpts,
3582e893c4SAbraham Corea Diaz SarifDocumentWriter *Writer)
360e5813b8SAbraham Corea Diaz : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {}
3782e893c4SAbraham Corea Diaz
3882e893c4SAbraham Corea Diaz // FIXME(llvm-project/issues/57323): Refactor Diagnostic classes.
emitDiagnosticMessage(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,StringRef Message,ArrayRef<clang::CharSourceRange> Ranges,DiagOrStoredDiag D)3982e893c4SAbraham Corea Diaz void SARIFDiagnostic::emitDiagnosticMessage(
4082e893c4SAbraham Corea Diaz FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level,
4182e893c4SAbraham Corea Diaz StringRef Message, ArrayRef<clang::CharSourceRange> Ranges,
4282e893c4SAbraham Corea Diaz DiagOrStoredDiag D) {
4382e893c4SAbraham Corea Diaz
4482e893c4SAbraham Corea Diaz const auto *Diag = D.dyn_cast<const Diagnostic *>();
4582e893c4SAbraham Corea Diaz
4682e893c4SAbraham Corea Diaz if (!Diag)
4782e893c4SAbraham Corea Diaz return;
4882e893c4SAbraham Corea Diaz
4982e893c4SAbraham Corea Diaz SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID()));
5082e893c4SAbraham Corea Diaz
5182e893c4SAbraham Corea Diaz Rule = addDiagnosticLevelToRule(Rule, Level);
5282e893c4SAbraham Corea Diaz
5382e893c4SAbraham Corea Diaz unsigned RuleIdx = Writer->createRule(Rule);
5482e893c4SAbraham Corea Diaz
5582e893c4SAbraham Corea Diaz SarifResult Result =
5682e893c4SAbraham Corea Diaz SarifResult::create(RuleIdx).setDiagnosticMessage(Message);
5782e893c4SAbraham Corea Diaz
5882e893c4SAbraham Corea Diaz if (Loc.isValid())
5982e893c4SAbraham Corea Diaz Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag);
6082e893c4SAbraham Corea Diaz
6182e893c4SAbraham Corea Diaz Writer->appendResult(Result);
6282e893c4SAbraham Corea Diaz }
6382e893c4SAbraham Corea Diaz
addLocationToResult(SarifResult Result,FullSourceLoc Loc,PresumedLoc PLoc,ArrayRef<CharSourceRange> Ranges,const Diagnostic & Diag)6482e893c4SAbraham Corea Diaz SarifResult SARIFDiagnostic::addLocationToResult(
6582e893c4SAbraham Corea Diaz SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc,
6682e893c4SAbraham Corea Diaz ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) {
6782e893c4SAbraham Corea Diaz SmallVector<CharSourceRange> Locations = {};
6882e893c4SAbraham Corea Diaz
6982e893c4SAbraham Corea Diaz if (PLoc.isInvalid()) {
7082e893c4SAbraham Corea Diaz // At least add the file name if available:
7182e893c4SAbraham Corea Diaz FileID FID = Loc.getFileID();
7282e893c4SAbraham Corea Diaz if (FID.isValid()) {
73523c4712SJan Svoboda if (OptionalFileEntryRef FE = Loc.getFileEntryRef()) {
7482e893c4SAbraham Corea Diaz emitFilename(FE->getName(), Loc.getManager());
7582e893c4SAbraham Corea Diaz // FIXME(llvm-project/issues/57366): File-only locations
7682e893c4SAbraham Corea Diaz }
7782e893c4SAbraham Corea Diaz }
7882e893c4SAbraham Corea Diaz return Result;
7982e893c4SAbraham Corea Diaz }
8082e893c4SAbraham Corea Diaz
8182e893c4SAbraham Corea Diaz FileID CaretFileID = Loc.getExpansionLoc().getFileID();
8282e893c4SAbraham Corea Diaz
8382e893c4SAbraham Corea Diaz for (const CharSourceRange Range : Ranges) {
8482e893c4SAbraham Corea Diaz // Ignore invalid ranges.
8582e893c4SAbraham Corea Diaz if (Range.isInvalid())
8682e893c4SAbraham Corea Diaz continue;
8782e893c4SAbraham Corea Diaz
8882e893c4SAbraham Corea Diaz auto &SM = Loc.getManager();
8982e893c4SAbraham Corea Diaz SourceLocation B = SM.getExpansionLoc(Range.getBegin());
9082e893c4SAbraham Corea Diaz CharSourceRange ERange = SM.getExpansionRange(Range.getEnd());
9182e893c4SAbraham Corea Diaz SourceLocation E = ERange.getEnd();
9282e893c4SAbraham Corea Diaz bool IsTokenRange = ERange.isTokenRange();
9382e893c4SAbraham Corea Diaz
9482e893c4SAbraham Corea Diaz std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B);
9582e893c4SAbraham Corea Diaz std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E);
9682e893c4SAbraham Corea Diaz
9782e893c4SAbraham Corea Diaz // If the start or end of the range is in another file, just discard
9882e893c4SAbraham Corea Diaz // it.
9982e893c4SAbraham Corea Diaz if (BInfo.first != CaretFileID || EInfo.first != CaretFileID)
10082e893c4SAbraham Corea Diaz continue;
10182e893c4SAbraham Corea Diaz
10282e893c4SAbraham Corea Diaz // Add in the length of the token, so that we cover multi-char
10382e893c4SAbraham Corea Diaz // tokens.
10482e893c4SAbraham Corea Diaz unsigned TokSize = 0;
10582e893c4SAbraham Corea Diaz if (IsTokenRange)
10682e893c4SAbraham Corea Diaz TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts);
10782e893c4SAbraham Corea Diaz
10882e893c4SAbraham Corea Diaz FullSourceLoc BF(B, SM), EF(E, SM);
10982e893c4SAbraham Corea Diaz SourceLocation BeginLoc = SM.translateLineCol(
11082e893c4SAbraham Corea Diaz BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber());
11182e893c4SAbraham Corea Diaz SourceLocation EndLoc = SM.translateLineCol(
11282e893c4SAbraham Corea Diaz EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize);
11382e893c4SAbraham Corea Diaz
11482e893c4SAbraham Corea Diaz Locations.push_back(
11582e893c4SAbraham Corea Diaz CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false});
11682e893c4SAbraham Corea Diaz // FIXME: Additional ranges should use presumed location in both
11782e893c4SAbraham Corea Diaz // Text and SARIF diagnostics.
11882e893c4SAbraham Corea Diaz }
11982e893c4SAbraham Corea Diaz
12082e893c4SAbraham Corea Diaz auto &SM = Loc.getManager();
12182e893c4SAbraham Corea Diaz auto FID = PLoc.getFileID();
12282e893c4SAbraham Corea Diaz // Visual Studio 2010 or earlier expects column number to be off by one.
12382e893c4SAbraham Corea Diaz unsigned int ColNo = (LangOpts.MSCompatibilityVersion &&
12482e893c4SAbraham Corea Diaz !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012))
12582e893c4SAbraham Corea Diaz ? PLoc.getColumn() - 1
12682e893c4SAbraham Corea Diaz : PLoc.getColumn();
12782e893c4SAbraham Corea Diaz SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo);
12882e893c4SAbraham Corea Diaz
12982e893c4SAbraham Corea Diaz // FIXME(llvm-project/issues/57366): Properly process #line directives.
13082e893c4SAbraham Corea Diaz Locations.push_back(
13182e893c4SAbraham Corea Diaz CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false});
13282e893c4SAbraham Corea Diaz
13382e893c4SAbraham Corea Diaz return Result.setLocations(Locations);
13482e893c4SAbraham Corea Diaz }
13582e893c4SAbraham Corea Diaz
13682e893c4SAbraham Corea Diaz SarifRule
addDiagnosticLevelToRule(SarifRule Rule,DiagnosticsEngine::Level Level)13782e893c4SAbraham Corea Diaz SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule,
13882e893c4SAbraham Corea Diaz DiagnosticsEngine::Level Level) {
13982e893c4SAbraham Corea Diaz auto Config = SarifReportingConfiguration::create();
14082e893c4SAbraham Corea Diaz
14182e893c4SAbraham Corea Diaz switch (Level) {
14282e893c4SAbraham Corea Diaz case DiagnosticsEngine::Note:
14382e893c4SAbraham Corea Diaz Config = Config.setLevel(SarifResultLevel::Note);
14482e893c4SAbraham Corea Diaz break;
14582e893c4SAbraham Corea Diaz case DiagnosticsEngine::Remark:
14682e893c4SAbraham Corea Diaz Config = Config.setLevel(SarifResultLevel::None);
14782e893c4SAbraham Corea Diaz break;
14882e893c4SAbraham Corea Diaz case DiagnosticsEngine::Warning:
14982e893c4SAbraham Corea Diaz Config = Config.setLevel(SarifResultLevel::Warning);
15082e893c4SAbraham Corea Diaz break;
15182e893c4SAbraham Corea Diaz case DiagnosticsEngine::Error:
15282e893c4SAbraham Corea Diaz Config = Config.setLevel(SarifResultLevel::Error).setRank(50);
15382e893c4SAbraham Corea Diaz break;
15482e893c4SAbraham Corea Diaz case DiagnosticsEngine::Fatal:
15582e893c4SAbraham Corea Diaz Config = Config.setLevel(SarifResultLevel::Error).setRank(100);
15682e893c4SAbraham Corea Diaz break;
15782e893c4SAbraham Corea Diaz case DiagnosticsEngine::Ignored:
15882e893c4SAbraham Corea Diaz assert(false && "Invalid diagnostic type");
15982e893c4SAbraham Corea Diaz }
16082e893c4SAbraham Corea Diaz
16182e893c4SAbraham Corea Diaz return Rule.setDefaultConfiguration(Config);
16282e893c4SAbraham Corea Diaz }
16382e893c4SAbraham Corea Diaz
emitFilename(StringRef Filename,const SourceManager & SM)16482e893c4SAbraham Corea Diaz llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename,
16582e893c4SAbraham Corea Diaz const SourceManager &SM) {
16682e893c4SAbraham Corea Diaz if (DiagOpts->AbsolutePath) {
167*5a3130e3SJan Svoboda auto File = SM.getFileManager().getOptionalFileRef(Filename);
16882e893c4SAbraham Corea Diaz if (File) {
16982e893c4SAbraham Corea Diaz // We want to print a simplified absolute path, i. e. without "dots".
17082e893c4SAbraham Corea Diaz //
17182e893c4SAbraham Corea Diaz // The hardest part here are the paths like "<part1>/<link>/../<part2>".
17282e893c4SAbraham Corea Diaz // On Unix-like systems, we cannot just collapse "<link>/..", because
17382e893c4SAbraham Corea Diaz // paths are resolved sequentially, and, thereby, the path
17482e893c4SAbraham Corea Diaz // "<part1>/<part2>" may point to a different location. That is why
17582e893c4SAbraham Corea Diaz // we use FileManager::getCanonicalName(), which expands all indirections
17682e893c4SAbraham Corea Diaz // with llvm::sys::fs::real_path() and caches the result.
17782e893c4SAbraham Corea Diaz //
17882e893c4SAbraham Corea Diaz // On the other hand, it would be better to preserve as much of the
17982e893c4SAbraham Corea Diaz // original path as possible, because that helps a user to recognize it.
18082e893c4SAbraham Corea Diaz // real_path() expands all links, which is sometimes too much. Luckily,
18182e893c4SAbraham Corea Diaz // on Windows we can just use llvm::sys::path::remove_dots(), because,
18282e893c4SAbraham Corea Diaz // on that system, both aforementioned paths point to the same place.
18382e893c4SAbraham Corea Diaz #ifdef _WIN32
184*5a3130e3SJan Svoboda SmallString<256> TmpFilename = File->getName();
18582e893c4SAbraham Corea Diaz llvm::sys::fs::make_absolute(TmpFilename);
18682e893c4SAbraham Corea Diaz llvm::sys::path::native(TmpFilename);
18782e893c4SAbraham Corea Diaz llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true);
18882e893c4SAbraham Corea Diaz Filename = StringRef(TmpFilename.data(), TmpFilename.size());
18982e893c4SAbraham Corea Diaz #else
19082e893c4SAbraham Corea Diaz Filename = SM.getFileManager().getCanonicalName(*File);
19182e893c4SAbraham Corea Diaz #endif
19282e893c4SAbraham Corea Diaz }
19382e893c4SAbraham Corea Diaz }
19482e893c4SAbraham Corea Diaz
19582e893c4SAbraham Corea Diaz return Filename;
19682e893c4SAbraham Corea Diaz }
19782e893c4SAbraham Corea Diaz
19882e893c4SAbraham Corea Diaz /// Print out the file/line/column information and include trace.
19982e893c4SAbraham Corea Diaz ///
20082e893c4SAbraham Corea Diaz /// This method handlen the emission of the diagnostic location information.
20182e893c4SAbraham Corea Diaz /// This includes extracting as much location information as is present for
20282e893c4SAbraham Corea Diaz /// the diagnostic and printing it, as well as any include stack or source
20382e893c4SAbraham Corea Diaz /// ranges necessary.
emitDiagnosticLoc(FullSourceLoc Loc,PresumedLoc PLoc,DiagnosticsEngine::Level Level,ArrayRef<CharSourceRange> Ranges)20482e893c4SAbraham Corea Diaz void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
20582e893c4SAbraham Corea Diaz DiagnosticsEngine::Level Level,
20682e893c4SAbraham Corea Diaz ArrayRef<CharSourceRange> Ranges) {
20782e893c4SAbraham Corea Diaz assert(false && "Not implemented in SARIF mode");
20882e893c4SAbraham Corea Diaz }
20982e893c4SAbraham Corea Diaz
emitIncludeLocation(FullSourceLoc Loc,PresumedLoc PLoc)21082e893c4SAbraham Corea Diaz void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
21182e893c4SAbraham Corea Diaz assert(false && "Not implemented in SARIF mode");
21282e893c4SAbraham Corea Diaz }
21382e893c4SAbraham Corea Diaz
emitImportLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)21482e893c4SAbraham Corea Diaz void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
21582e893c4SAbraham Corea Diaz StringRef ModuleName) {
21682e893c4SAbraham Corea Diaz assert(false && "Not implemented in SARIF mode");
21782e893c4SAbraham Corea Diaz }
21882e893c4SAbraham Corea Diaz
emitBuildingModuleLocation(FullSourceLoc Loc,PresumedLoc PLoc,StringRef ModuleName)21982e893c4SAbraham Corea Diaz void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc,
22082e893c4SAbraham Corea Diaz PresumedLoc PLoc,
22182e893c4SAbraham Corea Diaz StringRef ModuleName) {
22282e893c4SAbraham Corea Diaz assert(false && "Not implemented in SARIF mode");
22382e893c4SAbraham Corea Diaz }
22482e893c4SAbraham Corea Diaz } // namespace clang
225