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