1//===- Diagnostic.cpp -----------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include <mcld/LD/Diagnostic.h>
10#include <llvm/Support/ErrorHandling.h>
11#include <llvm/Support/raw_ostream.h>
12#include <llvm/ADT/Twine.h>
13#include <ctype.h>
14#include <algorithm>
15
16using namespace mcld;
17
18//===----------------------------------------------------------------------===//
19//  Diagnostic
20Diagnostic::Diagnostic(DiagnosticEngine& pEngine)
21  : m_Engine(pEngine) {
22}
23
24Diagnostic::~Diagnostic()
25{
26}
27
28// format - format this diagnostic into string, subsituting the formal
29// arguments. The result is appended at on the pOutStr.
30void Diagnostic::format(std::string& pOutStr) const
31{
32  // we've not implemented DWARF LOC messages yet. So, keep pIsLoC false
33  llvm::StringRef desc = m_Engine.infoMap().getDescription(getID(), false);
34
35  format(desc.begin(), desc.end(), pOutStr);
36}
37
38const char* Diagnostic::findMatch(char pVal,
39                                  const char* pBegin, const char* pEnd ) const
40{
41  unsigned int depth = 0;
42  for (; pBegin != pEnd; ++pBegin) {
43    if (0 == depth && *pBegin == pVal)
44      return pBegin;
45    if (0 != depth && *pBegin == '}')
46      --depth;
47
48    if ('%' == *pBegin) {
49      ++pBegin;
50      if (pBegin == pEnd)
51        break;
52
53      if (!isdigit(*pBegin) && !ispunct(*pBegin)) {
54        ++pBegin;
55        while (pBegin != pEnd && !isdigit(*pBegin) && *pBegin != '{')
56          ++pBegin;
57
58        if (pBegin == pEnd)
59          break;
60        if ('{' == *pBegin)
61          ++depth;
62      }
63    }
64  } // end of for
65  return pEnd;
66}
67
68// format - format the given formal string, subsituting the formal
69// arguments. The result is appended at on the pOutStr.
70void Diagnostic::format(const char* pBegin, const char* pEnd,
71                        std::string& pOutStr) const
72{
73  const char* cur_char = pBegin;
74  while (cur_char != pEnd) {
75    if ('%' != *cur_char) {
76      const char* new_end = std::find(cur_char, pEnd, '%');
77      pOutStr.append(cur_char, new_end);
78      cur_char = new_end;
79      continue;
80    }
81    else if (ispunct(cur_char[1])) {
82      pOutStr.push_back(cur_char[1]);  // %% -> %.
83      cur_char += 2;
84      continue;
85    }
86
87    // skip the %.
88    ++cur_char;
89
90    const char* modifier = NULL, *argument = NULL;
91    size_t modifier_len = 0, argument_len = 0;
92
93    // we get a modifier
94    if (!isdigit(*cur_char)) {
95      modifier = cur_char;
96      while (*cur_char == '-' || (*cur_char >= 'a' && *cur_char <= 'z'))
97        ++cur_char;
98      modifier_len = cur_char - modifier;
99
100      // we get an argument
101      if ('{' == *cur_char) {
102        ++cur_char; // skip '{'
103        argument = cur_char;
104        cur_char = findMatch('}', cur_char, pEnd);
105
106        if (cur_char == pEnd) {
107          // DIAG's format error
108          llvm::report_fatal_error(llvm::Twine("Mismatched {} in the diagnostic: ") +
109                                   llvm::Twine(getID()));
110        }
111
112        argument_len = cur_char - argument;
113        ++cur_char; // skip '}'
114      }
115    }
116    if (!isdigit(*cur_char)) {
117      llvm::report_fatal_error(llvm::Twine("In diagnostic: ") +
118                               llvm::Twine(getID()) + llvm::Twine(": ") +
119                               llvm::Twine(pBegin) +
120                               llvm::Twine("\nNo given arugment number:\n"));
121    }
122
123    unsigned int arg_no = *cur_char - '0';
124    ++cur_char; // skip argument number
125
126    DiagnosticEngine::ArgumentKind kind = getArgKind(arg_no);
127    switch (kind) {
128      case DiagnosticEngine::ak_std_string: {
129        if (0 != modifier_len) {
130          llvm::report_fatal_error(llvm::Twine("In diagnostic: ") +
131                                   llvm::Twine(getID()) +
132                                   llvm::Twine(": ") + llvm::Twine(pBegin) +
133                                   llvm::Twine("\nNo modifiers for strings yet\n"));
134        }
135        const std::string& str = getArgStdStr(arg_no);
136        pOutStr.append(str.begin(), str.end());
137        break;
138      }
139      case DiagnosticEngine::ak_c_string: {
140        if (0 != modifier_len) {
141          llvm::report_fatal_error(llvm::Twine("In diagnostic: ") +
142                                   llvm::Twine(getID()) +
143                                   llvm::Twine(": ") + llvm::Twine(pBegin) +
144                                   llvm::Twine("\nNo modifiers for strings yet\n"));
145        }
146        const char* str = getArgCStr(arg_no);
147        if (NULL == str)
148          str = "(null)";
149        pOutStr.append(str);
150        break;
151      }
152      case DiagnosticEngine::ak_sint: {
153        int val = getArgSInt(arg_no);
154        llvm::raw_string_ostream(pOutStr) << val;
155        break;
156      }
157      case DiagnosticEngine::ak_uint: {
158        unsigned int val = getArgUInt(arg_no);
159        llvm::raw_string_ostream(pOutStr) << val;
160        break;
161      }
162      case DiagnosticEngine::ak_bool: {
163        bool val = getArgBool(arg_no);
164        if (val)
165          pOutStr.append("true");
166        else
167          pOutStr.append("false");
168        break;
169      }
170    } // end of switch
171  } // end of while
172}
173
174