1*bdd1243dSDimitry Andric //===--------- SARIFDiagnostic.cpp - SARIF Diagnostic Formatting ----------===// 2*bdd1243dSDimitry Andric // 3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*bdd1243dSDimitry Andric // 7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8*bdd1243dSDimitry Andric 9*bdd1243dSDimitry Andric #include "clang/Frontend/SARIFDiagnostic.h" 10*bdd1243dSDimitry Andric #include "clang/Basic/CharInfo.h" 11*bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticOptions.h" 12*bdd1243dSDimitry Andric #include "clang/Basic/FileManager.h" 13*bdd1243dSDimitry Andric #include "clang/Basic/Sarif.h" 14*bdd1243dSDimitry Andric #include "clang/Basic/SourceLocation.h" 15*bdd1243dSDimitry Andric #include "clang/Basic/SourceManager.h" 16*bdd1243dSDimitry Andric #include "clang/Lex/Lexer.h" 17*bdd1243dSDimitry Andric #include "llvm/ADT/ArrayRef.h" 18*bdd1243dSDimitry Andric #include "llvm/ADT/SmallString.h" 19*bdd1243dSDimitry Andric #include "llvm/ADT/StringExtras.h" 20*bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h" 21*bdd1243dSDimitry Andric #include "llvm/Support/Casting.h" 22*bdd1243dSDimitry Andric #include "llvm/Support/ConvertUTF.h" 23*bdd1243dSDimitry Andric #include "llvm/Support/ErrorHandling.h" 24*bdd1243dSDimitry Andric #include "llvm/Support/ErrorOr.h" 25*bdd1243dSDimitry Andric #include "llvm/Support/Locale.h" 26*bdd1243dSDimitry Andric #include "llvm/Support/Path.h" 27*bdd1243dSDimitry Andric #include "llvm/Support/raw_ostream.h" 28*bdd1243dSDimitry Andric #include <algorithm> 29*bdd1243dSDimitry Andric #include <string> 30*bdd1243dSDimitry Andric 31*bdd1243dSDimitry Andric namespace clang { 32*bdd1243dSDimitry Andric 33*bdd1243dSDimitry Andric SARIFDiagnostic::SARIFDiagnostic(raw_ostream &OS, const LangOptions &LangOpts, 34*bdd1243dSDimitry Andric DiagnosticOptions *DiagOpts, 35*bdd1243dSDimitry Andric SarifDocumentWriter *Writer) 36*bdd1243dSDimitry Andric : DiagnosticRenderer(LangOpts, DiagOpts), Writer(Writer) {} 37*bdd1243dSDimitry Andric 38*bdd1243dSDimitry Andric // FIXME(llvm-project/issues/57323): Refactor Diagnostic classes. 39*bdd1243dSDimitry Andric void SARIFDiagnostic::emitDiagnosticMessage( 40*bdd1243dSDimitry Andric FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, 41*bdd1243dSDimitry Andric StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, 42*bdd1243dSDimitry Andric DiagOrStoredDiag D) { 43*bdd1243dSDimitry Andric 44*bdd1243dSDimitry Andric const auto *Diag = D.dyn_cast<const Diagnostic *>(); 45*bdd1243dSDimitry Andric 46*bdd1243dSDimitry Andric if (!Diag) 47*bdd1243dSDimitry Andric return; 48*bdd1243dSDimitry Andric 49*bdd1243dSDimitry Andric SarifRule Rule = SarifRule::create().setRuleId(std::to_string(Diag->getID())); 50*bdd1243dSDimitry Andric 51*bdd1243dSDimitry Andric Rule = addDiagnosticLevelToRule(Rule, Level); 52*bdd1243dSDimitry Andric 53*bdd1243dSDimitry Andric unsigned RuleIdx = Writer->createRule(Rule); 54*bdd1243dSDimitry Andric 55*bdd1243dSDimitry Andric SarifResult Result = 56*bdd1243dSDimitry Andric SarifResult::create(RuleIdx).setDiagnosticMessage(Message); 57*bdd1243dSDimitry Andric 58*bdd1243dSDimitry Andric if (Loc.isValid()) 59*bdd1243dSDimitry Andric Result = addLocationToResult(Result, Loc, PLoc, Ranges, *Diag); 60*bdd1243dSDimitry Andric 61*bdd1243dSDimitry Andric Writer->appendResult(Result); 62*bdd1243dSDimitry Andric } 63*bdd1243dSDimitry Andric 64*bdd1243dSDimitry Andric SarifResult SARIFDiagnostic::addLocationToResult( 65*bdd1243dSDimitry Andric SarifResult Result, FullSourceLoc Loc, PresumedLoc PLoc, 66*bdd1243dSDimitry Andric ArrayRef<CharSourceRange> Ranges, const Diagnostic &Diag) { 67*bdd1243dSDimitry Andric SmallVector<CharSourceRange> Locations = {}; 68*bdd1243dSDimitry Andric 69*bdd1243dSDimitry Andric if (PLoc.isInvalid()) { 70*bdd1243dSDimitry Andric // At least add the file name if available: 71*bdd1243dSDimitry Andric FileID FID = Loc.getFileID(); 72*bdd1243dSDimitry Andric if (FID.isValid()) { 73*bdd1243dSDimitry Andric if (const FileEntry *FE = Loc.getFileEntry()) { 74*bdd1243dSDimitry Andric emitFilename(FE->getName(), Loc.getManager()); 75*bdd1243dSDimitry Andric // FIXME(llvm-project/issues/57366): File-only locations 76*bdd1243dSDimitry Andric } 77*bdd1243dSDimitry Andric } 78*bdd1243dSDimitry Andric return Result; 79*bdd1243dSDimitry Andric } 80*bdd1243dSDimitry Andric 81*bdd1243dSDimitry Andric FileID CaretFileID = Loc.getExpansionLoc().getFileID(); 82*bdd1243dSDimitry Andric 83*bdd1243dSDimitry Andric for (const CharSourceRange Range : Ranges) { 84*bdd1243dSDimitry Andric // Ignore invalid ranges. 85*bdd1243dSDimitry Andric if (Range.isInvalid()) 86*bdd1243dSDimitry Andric continue; 87*bdd1243dSDimitry Andric 88*bdd1243dSDimitry Andric auto &SM = Loc.getManager(); 89*bdd1243dSDimitry Andric SourceLocation B = SM.getExpansionLoc(Range.getBegin()); 90*bdd1243dSDimitry Andric CharSourceRange ERange = SM.getExpansionRange(Range.getEnd()); 91*bdd1243dSDimitry Andric SourceLocation E = ERange.getEnd(); 92*bdd1243dSDimitry Andric bool IsTokenRange = ERange.isTokenRange(); 93*bdd1243dSDimitry Andric 94*bdd1243dSDimitry Andric std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); 95*bdd1243dSDimitry Andric std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); 96*bdd1243dSDimitry Andric 97*bdd1243dSDimitry Andric // If the start or end of the range is in another file, just discard 98*bdd1243dSDimitry Andric // it. 99*bdd1243dSDimitry Andric if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) 100*bdd1243dSDimitry Andric continue; 101*bdd1243dSDimitry Andric 102*bdd1243dSDimitry Andric // Add in the length of the token, so that we cover multi-char 103*bdd1243dSDimitry Andric // tokens. 104*bdd1243dSDimitry Andric unsigned TokSize = 0; 105*bdd1243dSDimitry Andric if (IsTokenRange) 106*bdd1243dSDimitry Andric TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); 107*bdd1243dSDimitry Andric 108*bdd1243dSDimitry Andric FullSourceLoc BF(B, SM), EF(E, SM); 109*bdd1243dSDimitry Andric SourceLocation BeginLoc = SM.translateLineCol( 110*bdd1243dSDimitry Andric BF.getFileID(), BF.getLineNumber(), BF.getColumnNumber()); 111*bdd1243dSDimitry Andric SourceLocation EndLoc = SM.translateLineCol( 112*bdd1243dSDimitry Andric EF.getFileID(), EF.getLineNumber(), EF.getColumnNumber() + TokSize); 113*bdd1243dSDimitry Andric 114*bdd1243dSDimitry Andric Locations.push_back( 115*bdd1243dSDimitry Andric CharSourceRange{SourceRange{BeginLoc, EndLoc}, /* ITR = */ false}); 116*bdd1243dSDimitry Andric // FIXME: Additional ranges should use presumed location in both 117*bdd1243dSDimitry Andric // Text and SARIF diagnostics. 118*bdd1243dSDimitry Andric } 119*bdd1243dSDimitry Andric 120*bdd1243dSDimitry Andric auto &SM = Loc.getManager(); 121*bdd1243dSDimitry Andric auto FID = PLoc.getFileID(); 122*bdd1243dSDimitry Andric // Visual Studio 2010 or earlier expects column number to be off by one. 123*bdd1243dSDimitry Andric unsigned int ColNo = (LangOpts.MSCompatibilityVersion && 124*bdd1243dSDimitry Andric !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) 125*bdd1243dSDimitry Andric ? PLoc.getColumn() - 1 126*bdd1243dSDimitry Andric : PLoc.getColumn(); 127*bdd1243dSDimitry Andric SourceLocation DiagLoc = SM.translateLineCol(FID, PLoc.getLine(), ColNo); 128*bdd1243dSDimitry Andric 129*bdd1243dSDimitry Andric // FIXME(llvm-project/issues/57366): Properly process #line directives. 130*bdd1243dSDimitry Andric Locations.push_back( 131*bdd1243dSDimitry Andric CharSourceRange{SourceRange{DiagLoc, DiagLoc}, /* ITR = */ false}); 132*bdd1243dSDimitry Andric 133*bdd1243dSDimitry Andric return Result.setLocations(Locations); 134*bdd1243dSDimitry Andric } 135*bdd1243dSDimitry Andric 136*bdd1243dSDimitry Andric SarifRule 137*bdd1243dSDimitry Andric SARIFDiagnostic::addDiagnosticLevelToRule(SarifRule Rule, 138*bdd1243dSDimitry Andric DiagnosticsEngine::Level Level) { 139*bdd1243dSDimitry Andric auto Config = SarifReportingConfiguration::create(); 140*bdd1243dSDimitry Andric 141*bdd1243dSDimitry Andric switch (Level) { 142*bdd1243dSDimitry Andric case DiagnosticsEngine::Note: 143*bdd1243dSDimitry Andric Config = Config.setLevel(SarifResultLevel::Note); 144*bdd1243dSDimitry Andric break; 145*bdd1243dSDimitry Andric case DiagnosticsEngine::Remark: 146*bdd1243dSDimitry Andric Config = Config.setLevel(SarifResultLevel::None); 147*bdd1243dSDimitry Andric break; 148*bdd1243dSDimitry Andric case DiagnosticsEngine::Warning: 149*bdd1243dSDimitry Andric Config = Config.setLevel(SarifResultLevel::Warning); 150*bdd1243dSDimitry Andric break; 151*bdd1243dSDimitry Andric case DiagnosticsEngine::Error: 152*bdd1243dSDimitry Andric Config = Config.setLevel(SarifResultLevel::Error).setRank(50); 153*bdd1243dSDimitry Andric break; 154*bdd1243dSDimitry Andric case DiagnosticsEngine::Fatal: 155*bdd1243dSDimitry Andric Config = Config.setLevel(SarifResultLevel::Error).setRank(100); 156*bdd1243dSDimitry Andric break; 157*bdd1243dSDimitry Andric case DiagnosticsEngine::Ignored: 158*bdd1243dSDimitry Andric assert(false && "Invalid diagnostic type"); 159*bdd1243dSDimitry Andric } 160*bdd1243dSDimitry Andric 161*bdd1243dSDimitry Andric return Rule.setDefaultConfiguration(Config); 162*bdd1243dSDimitry Andric } 163*bdd1243dSDimitry Andric 164*bdd1243dSDimitry Andric llvm::StringRef SARIFDiagnostic::emitFilename(StringRef Filename, 165*bdd1243dSDimitry Andric const SourceManager &SM) { 166*bdd1243dSDimitry Andric if (DiagOpts->AbsolutePath) { 167*bdd1243dSDimitry Andric llvm::ErrorOr<const FileEntry *> File = 168*bdd1243dSDimitry Andric SM.getFileManager().getFile(Filename); 169*bdd1243dSDimitry Andric if (File) { 170*bdd1243dSDimitry Andric // We want to print a simplified absolute path, i. e. without "dots". 171*bdd1243dSDimitry Andric // 172*bdd1243dSDimitry Andric // The hardest part here are the paths like "<part1>/<link>/../<part2>". 173*bdd1243dSDimitry Andric // On Unix-like systems, we cannot just collapse "<link>/..", because 174*bdd1243dSDimitry Andric // paths are resolved sequentially, and, thereby, the path 175*bdd1243dSDimitry Andric // "<part1>/<part2>" may point to a different location. That is why 176*bdd1243dSDimitry Andric // we use FileManager::getCanonicalName(), which expands all indirections 177*bdd1243dSDimitry Andric // with llvm::sys::fs::real_path() and caches the result. 178*bdd1243dSDimitry Andric // 179*bdd1243dSDimitry Andric // On the other hand, it would be better to preserve as much of the 180*bdd1243dSDimitry Andric // original path as possible, because that helps a user to recognize it. 181*bdd1243dSDimitry Andric // real_path() expands all links, which is sometimes too much. Luckily, 182*bdd1243dSDimitry Andric // on Windows we can just use llvm::sys::path::remove_dots(), because, 183*bdd1243dSDimitry Andric // on that system, both aforementioned paths point to the same place. 184*bdd1243dSDimitry Andric #ifdef _WIN32 185*bdd1243dSDimitry Andric SmallString<256> TmpFilename = (*File)->getName(); 186*bdd1243dSDimitry Andric llvm::sys::fs::make_absolute(TmpFilename); 187*bdd1243dSDimitry Andric llvm::sys::path::native(TmpFilename); 188*bdd1243dSDimitry Andric llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true); 189*bdd1243dSDimitry Andric Filename = StringRef(TmpFilename.data(), TmpFilename.size()); 190*bdd1243dSDimitry Andric #else 191*bdd1243dSDimitry Andric Filename = SM.getFileManager().getCanonicalName(*File); 192*bdd1243dSDimitry Andric #endif 193*bdd1243dSDimitry Andric } 194*bdd1243dSDimitry Andric } 195*bdd1243dSDimitry Andric 196*bdd1243dSDimitry Andric return Filename; 197*bdd1243dSDimitry Andric } 198*bdd1243dSDimitry Andric 199*bdd1243dSDimitry Andric /// Print out the file/line/column information and include trace. 200*bdd1243dSDimitry Andric /// 201*bdd1243dSDimitry Andric /// This method handlen the emission of the diagnostic location information. 202*bdd1243dSDimitry Andric /// This includes extracting as much location information as is present for 203*bdd1243dSDimitry Andric /// the diagnostic and printing it, as well as any include stack or source 204*bdd1243dSDimitry Andric /// ranges necessary. 205*bdd1243dSDimitry Andric void SARIFDiagnostic::emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, 206*bdd1243dSDimitry Andric DiagnosticsEngine::Level Level, 207*bdd1243dSDimitry Andric ArrayRef<CharSourceRange> Ranges) { 208*bdd1243dSDimitry Andric assert(false && "Not implemented in SARIF mode"); 209*bdd1243dSDimitry Andric } 210*bdd1243dSDimitry Andric 211*bdd1243dSDimitry Andric void SARIFDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { 212*bdd1243dSDimitry Andric assert(false && "Not implemented in SARIF mode"); 213*bdd1243dSDimitry Andric } 214*bdd1243dSDimitry Andric 215*bdd1243dSDimitry Andric void SARIFDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, 216*bdd1243dSDimitry Andric StringRef ModuleName) { 217*bdd1243dSDimitry Andric assert(false && "Not implemented in SARIF mode"); 218*bdd1243dSDimitry Andric } 219*bdd1243dSDimitry Andric 220*bdd1243dSDimitry Andric void SARIFDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc, 221*bdd1243dSDimitry Andric PresumedLoc PLoc, 222*bdd1243dSDimitry Andric StringRef ModuleName) { 223*bdd1243dSDimitry Andric assert(false && "Not implemented in SARIF mode"); 224*bdd1243dSDimitry Andric } 225*bdd1243dSDimitry Andric } // namespace clang 226