xref: /llvm-project/clang/lib/Frontend/SARIFDiagnostic.cpp (revision 5a3130e3b645cf5fc179d9274eb1b62b7f0c7438)
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