Synopsis - Cross-Reference

File: /Synopsis/Parsers/Cxx/SXRGenerator.cc
  1//
  2// Copyright (C) 2008 Stefan Seefeld
  3// All rights reserved.
  4// Licensed to the public under the terms of the GNU LGPL (>= 2),
  5// see the file COPYING for details.
  6//
  7
  8#include <Python.h>
  9#include <Support/Path.hh>
 10
 11#include "Walker.hh"
 12#include "SXRGenerator.hh"
 13#include "SXRBuffer.hh"
 14#include "ASG.hh"
 15#include "Types.hh"
 16#include "TypeIdFormatter.hh"
 17#include "Builder.hh"
 18#include "STrace.hh"
 19#include "Filter.hh"
 20
 21#include <fstream>
 22#include <sstream>
 23#include <iomanip>
 24#include <map>
 25
 26#include <Synopsis/PTree.hh>
 27#include <Synopsis/Buffer.hh>
 28
 29namespace 
 30{
 31
 32char const *context_names[] =
 33{
 34  "reference",
 35  "definition",
 36  "SPAN",
 37  "IMPL",
 38  "UDIR",
 39  "UDEC",
 40  "call"
 41};
 42
 43}
 44
 45SXRGenerator::SXRGenerator(FileFilter* filter, Walker *walker)
 46  : filter_(filter),
 47    buffer_(walker->buffer()),
 48    walker_(walker)
 49
 50{
 51  // Check size of array here to prevent later segfaults
 52  assert(sizeof(context_names)/sizeof(context_names[0]) == NumContext);
 53}
 54
 55SXRGenerator::~SXRGenerator()
 56{
 57  for (SXRDict::iterator i = buffers_.begin(); i != buffers_.end(); ++i)
 58  {
 59    i->second->write();
 60    delete i->second;
 61  }
 62}
 63
 64Walker* SXRGenerator::walker()
 65{
 66  return walker_;
 67}
 68
 69int SXRGenerator::map_column(ASG::SourceFile *file, int line, char const *ptr)
 70{
 71  char const *line_start = ptr;
 72  while (line_start > buffer_->ptr() && *line_start != '\n') --line_start;
 73  int col = ptr - (line_start + 1);
 74  // Resolve macro maps
 75  return file->map_column(line, col);
 76}
 77
 78void SXRGenerator::xref(PTree::Node *node, Context context, ScopedName const &name, std::string const &desc, ASG::Declaration const *decl)
 79{
 80  walker_->update_line_number(node);
 81  ASG::SourceFile* file = walker_->current_file();
 82
 83  // Dont store records for included files
 84  if (!filter_->should_xref(file))
 85    return;
 86
 87  // Get info for storing a syntax record
 88  int begin_line = walker_->line_of_ptree(node);
 89  int begin_col = map_column(file, begin_line, node->begin());
 90
 91  if (begin_col < 0) return; // inside macro
 92
 93  std::string filename;
 94  unsigned long end_line = buffer_->origin(node->end(), filename);
 95
 96  if (begin_line == end_line)
 97  {
 98    int len = node->end() - node->begin();
 99    store_xref(file, begin_line, begin_col, len, context, name, desc, false);
100  }
101  else
102  {
103    // Generate one xref plus continuations for each additional line.
104    int end_col = map_column(file, end_line, node->end());
105    for (int line = begin_line; line < end_line; ++line, begin_col = 0)
106      store_xref(file, line, begin_col, -1, context, name, desc, line != begin_line);
107    store_xref(file, end_line, 0, end_col, context, name, desc, true);
108  }
109}
110
111//. A class which acts as a Types Visitor to store the correct link to a given
112//. type
113class TypeStorer : public Types::Visitor
114{
115  // Variables to pass to link()
116  SXRGenerator *sxr_;
117  PTree::Node *node;
118  SXRGenerator::Context context;
119public:
120  //. Constructor
121  TypeStorer(SXRGenerator* sxr, PTree::Node *n, SXRGenerator::Context c)
122    : sxr_(sxr), node(n), context(c)
123  {}
124
125  //. Returns a suitable description for the given type
126  std::string describe(Types::Type* type)
127  {
128    std::string desc;
129    try
130    {
131      return Types::declared_cast<ASG::Declaration>(type)->type();
132    }
133    catch (Types::wrong_type_cast const &)
134    {
135      return sxr_->walker()->type_formatter()->format(type);
136    }
137  }
138
139  // Visitor methods
140  void visit_base(Types::Base* base)
141  {
142    sxr_->span(node, "keyword");
143  }
144  void visit_dependent(Types::Dependent*)
145  {
146  }
147
148  void visit_named(Types::Named* named)
149  {
150    // All other nameds get stored
151    sxr_->xref(node, context, named->name(), describe(named));
152  }
153  void visit_declared(Types::Declared* declared)
154  {
155    // All other nameds get stored
156    sxr_->xref(node, context, declared->name(), describe(declared), declared->declaration());
157  }
158  void visit_modifier(Types::Modifier* mod)
159  {
160    // We recurse on the mod's alias, but dont link the const bit
161    if (mod->pre().size() && mod->pre().front() == "const")
162      if (!node->is_atom() && PTree::first(node) && *PTree::first(node) == "const")
163      {
164        sxr_->span(PTree::first(node), "keyword");
165        node = PTree::first(PTree::last(node));
166      }
167    mod->alias()->accept(this);
168  }
169  void visit_parameterized(Types::Parameterized* param)
170  {
171    // Sometimes there's a typename at the front..
172    if (PTree::first(node)->is_atom() && PTree::first(node) && *PTree::first(node) == "typename")
173      node = PTree::second(node);
174    // Some modifiers nest the identifier..
175    while (!PTree::first(node)->is_atom())
176      node = PTree::first(node);
177    // For qualified template names the ptree is:
178    //  [ std :: [ vector [ < ... , ... > ] ] ]
179    // If the name starts with :: (global scope), skip it
180    if (PTree::first(node) && *PTree::first(node) == "::")
181      node = PTree::rest(node);
182    // Skip the qualifieds (and just link the final name)
183    while (PTree::second(node) && *PTree::second(node) == "::")
184      if (PTree::third(node)->is_atom())
185        node = PTree::rest(PTree::rest(node));
186      else
187        node = PTree::third(node);
188    // Do template
189    sxr_->xref(PTree::first(node), param->template_id());
190    // Do params
191    node = PTree::second(node);
192    typedef Types::Type::vector::iterator iterator;
193    iterator iter = param->parameters().begin();
194    iterator end = param->parameters().end();
195    // Could be leaf if eg: [SomeId const] node is now "const"
196    while (node && !node->is_atom() && iter != end)
197    {
198      // Skip '<' or ','
199      if (!(node = PTree::rest(node)))
200        break;
201      if (node->car() && node->car()->car() && !node->car()->car()->is_atom() && node->car()->car()->car())
202        sxr_->xref(node->car()->car()->car(), *iter);
203      ++iter;
204      node = PTree::rest(node);
205    }
206  }
207  // Other types ignored, for now
208};
209
210// Store if type is named
211void SXRGenerator::xref(PTree::Node *node, Types::Type* type, Context context)
212{
213  ASG::SourceFile* file = walker_->current_file();
214  if (!type || !filter_->should_xref(file))
215    return;
216  TypeStorer storer(this, node, context);
217  type->accept(&storer);
218}
219
220void SXRGenerator::xref(PTree::Node *node, ASG::Declaration const *decl)
221{
222  ASG::SourceFile* file = walker_->current_file();
223  if (!decl || !filter_->should_xref(file))
224    return;
225  xref(node, Definition, decl->name(), decl->type(), decl);
226}
227
228void SXRGenerator::store_span(unsigned int line, unsigned int col, int len, char const *type)
229{
230  // TODO: desc maps to href title...
231  ASG::SourceFile* file = walker_->current_file();
232  if (!filter_->should_xref(file)) return;
233  SXRBuffer *sxr = get_buffer(file);
234  sxr->insert_span(line, col, len, type);
235}
236
237void SXRGenerator::span(PTree::Node *node, char const *desc)
238{
239  int line = walker_->line_of_ptree(node);
240  ASG::SourceFile* file = walker_->current_file();
241  if (!filter_->should_xref(file))
242    return;
243  int col = map_column(file, line, node->begin());
244  if (col < 0)
245    return; // inside macro
246  int len = node->end() - node->begin();
247  
248  store_span(line, col, len, desc);
249}
250
251void SXRGenerator::long_span(PTree::Node *node, char const *desc)
252{
253  // Find left edge
254  int left_line = walker_->line_of_ptree(node);
255  ASG::SourceFile* file = walker_->current_file();
256  if (!filter_->should_xref(file))
257    return;
258  int left_col = map_column(file, left_line, node->begin());
259  if (left_col < 0)
260    return; // inside macro
261  int len = node->end() - node->begin();
262  
263  // Find right edge
264  std::string filename;
265  unsigned long right_line = buffer_->origin(node->end(), filename);
266  
267  if (right_line == left_line)
268    // Same line, so normal output
269    store_span(left_line, left_col, len, desc);
270  else
271  {
272    // Must output one for each line
273    int right_col = map_column(file, right_line, node->end());
274    for (int line = left_line; line < right_line; line++, left_col = 0)
275      store_span(line, left_col, -1, desc);
276    // Lasg line is a bit different
277    store_span(right_line, 0, right_col, desc);
278  }
279}
280
281// Store a link in the Syntax File
282void SXRGenerator::store_xref(ASG::SourceFile* file,
283                              int line, int col, int len, Context context,
284                              ScopedName const &qname, std::string const &desc,
285                              bool continuation)
286{
287  SXRBuffer *sxr = get_buffer(file);
288  std::vector<ASG::Scope*> scopes;
289  Types::Named* vtype;
290  ScopedName name;
291  if (walker_->builder()->mapName(qname, scopes, vtype))
292  {
293    for (size_t i = 0; i < scopes.size(); i++)
294    {
295      if (ASG::Namespace* ns = dynamic_cast<ASG::Namespace*>(scopes[i]))
296        if (ns->type() == "function")
297        {
298          // Restart description at function scope
299          name.clear();
300          continue;
301        }
302      // Add the name to the short name
303      name.push_back(scopes[i]->name().back());
304    }
305    // Add the final type name to the short name
306    name.push_back(vtype->name().back());
307  }
308  else
309  {
310    STrace trace("SXRGenerator::xref");
311    LOG("WARNING: couldnt map name " << qname);
312    name = qname;
313  }
314  ASG::Scope *scope = walker_->builder()->scope();
315  std::string from = join(scope->name(), "::");
316  std::string type = context_names[context];
317  std::string title = desc + " " + join(name, "::");
318  sxr->insert_xref(line, col, len, join(qname, "::"), type, from, title, continuation);
319}
320
321SXRBuffer *SXRGenerator::get_buffer(ASG::SourceFile* file)
322{
323  SXRBuffer *buf = 0;
324  if (buffers_.find(file) == buffers_.end())
325  {
326    std::string filename = filter_->get_sxr_filename(file);
327    Synopsis::makedirs(Synopsis::Path(filename).dirname());
328    buf = new SXRBuffer(filename.c_str(), file->abs_name(), file->name());
329    buffers_.insert(std::make_pair(file, buf));
330  }
331  else
332    buf = buffers_[file];
333  return buf;
334}