Synopsis - Cross-Reference

File: /sandbox/call_graph/call-graph.cc
  1//
  2// Copyright (C) 2005 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#include <Synopsis/Buffer.hh>
  8#include <Synopsis/Lexer.hh>
  9#include <Synopsis/Parser.hh>
 10#include <Synopsis/PTree.hh>
 11#include <Synopsis/SymbolLookup.hh>
 12#include <Synopsis/PTree/Writer.hh>
 13#include <Synopsis/Trace.hh>
 14#include <fstream>
 15#include <set>
 16
 17using namespace Synopsis;
 18namespace PT = Synopsis::PTree;
 19namespace SL = Synopsis::SymbolLookup;
 20
 21class EnclosingFunctionFinder : private SL::ScopeVisitor
 22{
 23public:
 24  //. Find the nearest enclosing scope that is either
 25  //. a function or namespace (i.e. global scope).
 26  SL::Scope const *find(SL::Scope const *s)
 27  {
 28    my_scope = 0;
 29    const_cast<SL::Scope *>(s)->accept(this);
 30    return my_scope;
 31  }
 32private:
 33
 34  virtual void visit(SL::LocalScope *s) 
 35  { const_cast<SL::Scope *>(s->outer_scope())->accept(this);}
 36  virtual void visit(SL::FunctionScope *s) { my_scope = s;}
 37  virtual void visit(SL::Namespace *s) { my_scope = s;}
 38
 39  SL::Scope const *my_scope;
 40};
 41
 42//. A simple call graph generator.
 43//. This class finds function call expressions and
 44//. reports the (function) scope of the call together
 45//. with the name of the callee.
 46//. As this is only a demo of part of the Synopsis API
 47//. no attempt has been made for completeness, i.e. there
 48//. is no pointer analysis, etc.
 49//. Further, only C sources are supported as for C++
 50//. type analysis would be needed to do proper
 51//. overload resolution, which is not yet implemented.
 52class CallGraphGenerator : private SL::Walker
 53{
 54public:
 55  CallGraphGenerator(std::ostream &os, SL::Scope *symbols)
 56    : Walker(symbols), my_os(os) {}
 57  void generate(PT::Node const *node) 
 58  {
 59    my_os << "digraph CallGraph\n{\n"
 60	  << "node[fillcolor=\"#ffffcc\", pencolor=\"#424242\" style=\"filled\"];\n";
 61    const_cast<PT::Node *>(node)->accept(this);
 62    my_os << '}' << std::endl;
 63  }
 64private:
 65  void visit(PT::FuncallExpr *func)
 66  {
 67    PT::Node *name = PT::first(func);
 68
 69    EnclosingFunctionFinder function_finder;
 70    SL::Scope const *scope = function_finder.find(current_scope());
 71    std::string caller = "<global>";
 72    if (SL::FunctionScope const *func = dynamic_cast<SL::FunctionScope const *>(scope))
 73      caller = func->name();
 74    std::string callee = PT::reify(name);
 75    if (my_cache.find(caller) == my_cache.end())
 76    {
 77      my_cache.insert(caller);
 78      my_os << '"' << caller << "\" [label=\"" << caller << "\"];\n";
 79    }
 80    if (my_cache.find(callee) == my_cache.end())
 81    {
 82      my_cache.insert(callee);
 83      my_os << '"' << callee << "\" [label=\"" << callee << "\"];\n";
 84    }
 85    my_os << '"' << caller << "\" -> \"" << callee << '"' << std::endl;
 86  }
 87
 88  std::set<std::string> my_cache;
 89  std::ostream &        my_os;
 90};
 91
 92int usage(const char *command)
 93{
 94  std::cerr << "Usage: " << command 
 95	    << " [-C] [-d] [-o <output>] <input>" << std::endl;
 96  return -1;
 97}
 98
 99int main(int argc, char **argv)
100{
101  const char *input = argv[1];
102  std::ofstream output;
103  if (argc == 1) return usage(argv[0]);
104  input = argv[argc - 1];
105  bool cxx = false;
106  for (size_t i = 1; i != argc - 1; ++i)
107  {
108    if (argv[i] == std::string("-C")) cxx = true;
109    else if (argv[i] == std::string("-d")) Trace::enable();
110    else if (argv[i] == std::string("-o") && i < argc - 1)
111      output.open(argv[++i]);
112    else return usage(argv[0]);
113  }
114  if (!output.is_open())
115  {
116    output.copyfmt(std::cout);
117    output.clear(std::cout.rdstate());
118    static_cast<std::basic_ios<char> &>(output).rdbuf(std::cout.rdbuf());
119  }
120//   try
121  {
122    // FIXME: For now we have to initialize the Encoding...
123    PT::Encoding::do_init_static();
124
125    std::ifstream ifs(input);
126    Buffer buffer(ifs.rdbuf(), input);
127    int tokenset = Lexer::C | Lexer::GCC | Lexer::MSVC;
128    int ruleset = 0;
129    if (cxx)
130    {
131      tokenset |= Lexer::CXX;
132      ruleset = Parser::CXX | Parser::MSVC;
133    }
134    Lexer lexer(&buffer, tokenset);
135    SymbolFactory symbols;
136    Parser parser(lexer, symbols, ruleset);
137    PT::Node *node = parser.parse();
138    const Parser::ErrorList &errors = parser.errors();
139    for (Parser::ErrorList::const_iterator i = errors.begin(); i != errors.end(); ++i)
140      (*i)->write(std::cerr);
141
142    if (!node) return -1;
143    CallGraphGenerator generator(output, symbols.current_scope());
144    generator.generate(node);
145  }
146//   catch (const std::exception &e)
147//   {
148//     std::cerr << "Caught exception : " << e.what() << std::endl;
149//     throw;
150//   }
151}