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