| 1 | //=== SourceMgrAdapter.cpp - SourceMgr to SourceManager Adapter -----------===// |
| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file implements the adapter that maps diagnostics from llvm::SourceMgr |
| 10 | // to Clang's SourceManager. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "clang/Basic/SourceMgrAdapter.h" |
| 15 | #include "clang/Basic/Diagnostic.h" |
| 16 | |
| 17 | using namespace clang; |
| 18 | |
| 19 | void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &Diag, |
| 20 | void *Context) { |
| 21 | static_cast<SourceMgrAdapter *>(Context)->handleDiag(Diag); |
| 22 | } |
| 23 | |
| 24 | SourceMgrAdapter::SourceMgrAdapter(SourceManager &SM, |
| 25 | DiagnosticsEngine &Diagnostics, |
| 26 | unsigned ErrorDiagID, unsigned WarningDiagID, |
| 27 | unsigned NoteDiagID, |
| 28 | OptionalFileEntryRef DefaultFile) |
| 29 | : SrcMgr(SM), Diagnostics(Diagnostics), ErrorDiagID(ErrorDiagID), |
| 30 | WarningDiagID(WarningDiagID), NoteDiagID(NoteDiagID), |
| 31 | DefaultFile(DefaultFile) {} |
| 32 | |
| 33 | SourceMgrAdapter::~SourceMgrAdapter() {} |
| 34 | |
| 35 | SourceLocation SourceMgrAdapter::mapLocation(const llvm::SourceMgr &LLVMSrcMgr, |
| 36 | llvm::SMLoc Loc) { |
| 37 | // Map invalid locations. |
| 38 | if (!Loc.isValid()) |
| 39 | return SourceLocation(); |
| 40 | |
| 41 | // Find the buffer containing the location. |
| 42 | unsigned BufferID = LLVMSrcMgr.FindBufferContainingLoc(Loc); |
| 43 | if (!BufferID) |
| 44 | return SourceLocation(); |
| 45 | |
| 46 | // If we haven't seen this buffer before, copy it over. |
| 47 | auto Buffer = LLVMSrcMgr.getMemoryBuffer(i: BufferID); |
| 48 | auto KnownBuffer = FileIDMapping.find(Val: std::make_pair(x: &LLVMSrcMgr, y&: BufferID)); |
| 49 | if (KnownBuffer == FileIDMapping.end()) { |
| 50 | FileID FileID; |
| 51 | if (DefaultFile) { |
| 52 | // Map to the default file. |
| 53 | FileID = SrcMgr.getOrCreateFileID(SourceFile: *DefaultFile, FileCharacter: SrcMgr::C_User); |
| 54 | |
| 55 | // Only do this once. |
| 56 | DefaultFile = std::nullopt; |
| 57 | } else { |
| 58 | // Make a copy of the memory buffer. |
| 59 | StringRef bufferName = Buffer->getBufferIdentifier(); |
| 60 | auto bufferCopy = std::unique_ptr<llvm::MemoryBuffer>( |
| 61 | llvm::MemoryBuffer::getMemBufferCopy(InputData: Buffer->getBuffer(), |
| 62 | BufferName: bufferName)); |
| 63 | |
| 64 | // Add this memory buffer to the Clang source manager. |
| 65 | FileID = SrcMgr.createFileID(Buffer: std::move(bufferCopy)); |
| 66 | } |
| 67 | |
| 68 | // Save the mapping. |
| 69 | KnownBuffer = FileIDMapping |
| 70 | .insert(KV: std::make_pair( |
| 71 | x: std::make_pair(x: &LLVMSrcMgr, y&: BufferID), y&: FileID)) |
| 72 | .first; |
| 73 | } |
| 74 | |
| 75 | // Translate the offset into the file. |
| 76 | unsigned Offset = Loc.getPointer() - Buffer->getBufferStart(); |
| 77 | return SrcMgr.getLocForStartOfFile(FID: KnownBuffer->second) |
| 78 | .getLocWithOffset(Offset); |
| 79 | } |
| 80 | |
| 81 | SourceRange SourceMgrAdapter::mapRange(const llvm::SourceMgr &LLVMSrcMgr, |
| 82 | llvm::SMRange Range) { |
| 83 | if (!Range.isValid()) |
| 84 | return SourceRange(); |
| 85 | |
| 86 | SourceLocation Start = mapLocation(LLVMSrcMgr, Loc: Range.Start); |
| 87 | SourceLocation End = mapLocation(LLVMSrcMgr, Loc: Range.End); |
| 88 | return SourceRange(Start, End); |
| 89 | } |
| 90 | |
| 91 | void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &Diag) { |
| 92 | // Map the location. |
| 93 | SourceLocation Loc; |
| 94 | if (auto *LLVMSrcMgr = Diag.getSourceMgr()) |
| 95 | Loc = mapLocation(LLVMSrcMgr: *LLVMSrcMgr, Loc: Diag.getLoc()); |
| 96 | |
| 97 | // Extract the message. |
| 98 | StringRef Message = Diag.getMessage(); |
| 99 | |
| 100 | // Map the diagnostic kind. |
| 101 | unsigned DiagID; |
| 102 | switch (Diag.getKind()) { |
| 103 | case llvm::SourceMgr::DK_Error: |
| 104 | DiagID = ErrorDiagID; |
| 105 | break; |
| 106 | |
| 107 | case llvm::SourceMgr::DK_Warning: |
| 108 | DiagID = WarningDiagID; |
| 109 | break; |
| 110 | |
| 111 | case llvm::SourceMgr::DK_Remark: |
| 112 | llvm_unreachable("remarks not implemented" ); |
| 113 | |
| 114 | case llvm::SourceMgr::DK_Note: |
| 115 | DiagID = NoteDiagID; |
| 116 | break; |
| 117 | } |
| 118 | |
| 119 | // Report the diagnostic. |
| 120 | DiagnosticBuilder Builder = Diagnostics.Report(Loc, DiagID) << Message; |
| 121 | |
| 122 | if (auto *LLVMSrcMgr = Diag.getSourceMgr()) { |
| 123 | // Translate ranges. |
| 124 | SourceLocation StartOfLine = Loc.getLocWithOffset(Offset: -Diag.getColumnNo()); |
| 125 | for (auto Range : Diag.getRanges()) { |
| 126 | Builder << SourceRange(StartOfLine.getLocWithOffset(Offset: Range.first), |
| 127 | StartOfLine.getLocWithOffset(Offset: Range.second)); |
| 128 | } |
| 129 | |
| 130 | // Translate Fix-Its. |
| 131 | for (const llvm::SMFixIt &FixIt : Diag.getFixIts()) { |
| 132 | CharSourceRange Range(mapRange(LLVMSrcMgr: *LLVMSrcMgr, Range: FixIt.getRange()), false); |
| 133 | Builder << FixItHint::CreateReplacement(RemoveRange: Range, Code: FixIt.getText()); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |