Synopsis - Cross-Reference
File: /Synopsis/Parsers/Cxx/SXRGenerator.cc1// 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}