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 
handleDiag(const llvm::SMDiagnostic & Diag,void * Context)19 void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &Diag,
20                                   void *Context) {
21   static_cast<SourceMgrAdapter *>(Context)->handleDiag(Diag);
22 }
23 
SourceMgrAdapter(SourceManager & SM,DiagnosticsEngine & Diagnostics,unsigned ErrorDiagID,unsigned WarningDiagID,unsigned NoteDiagID,OptionalFileEntryRef DefaultFile)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 
~SourceMgrAdapter()33 SourceMgrAdapter::~SourceMgrAdapter() {}
34 
mapLocation(const llvm::SourceMgr & LLVMSrcMgr,llvm::SMLoc Loc)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(BufferID);
48   auto KnownBuffer = FileIDMapping.find(std::make_pair(&LLVMSrcMgr, BufferID));
49   if (KnownBuffer == FileIDMapping.end()) {
50     FileID FileID;
51     if (DefaultFile) {
52       // Map to the default file.
53       FileID = SrcMgr.getOrCreateFileID(*DefaultFile, 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(Buffer->getBuffer(),
62                                                bufferName));
63 
64       // Add this memory buffer to the Clang source manager.
65       FileID = SrcMgr.createFileID(std::move(bufferCopy));
66     }
67 
68     // Save the mapping.
69     KnownBuffer = FileIDMapping
70                       .insert(std::make_pair(
71                           std::make_pair(&LLVMSrcMgr, BufferID), FileID))
72                       .first;
73   }
74 
75   // Translate the offset into the file.
76   unsigned Offset = Loc.getPointer() - Buffer->getBufferStart();
77   return SrcMgr.getLocForStartOfFile(KnownBuffer->second)
78       .getLocWithOffset(Offset);
79 }
80 
mapRange(const llvm::SourceMgr & LLVMSrcMgr,llvm::SMRange Range)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, Range.Start);
87   SourceLocation End = mapLocation(LLVMSrcMgr, Range.End);
88   return SourceRange(Start, End);
89 }
90 
handleDiag(const llvm::SMDiagnostic & Diag)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, 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(-Diag.getColumnNo());
125     for (auto Range : Diag.getRanges()) {
126       Builder << SourceRange(StartOfLine.getLocWithOffset(Range.first),
127                              StartOfLine.getLocWithOffset(Range.second));
128     }
129 
130     // Translate Fix-Its.
131     for (const llvm::SMFixIt &FixIt : Diag.getFixIts()) {
132       CharSourceRange Range(mapRange(*LLVMSrcMgr, FixIt.getRange()), false);
133       Builder << FixItHint::CreateReplacement(Range, FixIt.getText());
134     }
135   }
136 }
137