6a1886cef59a8de1625716142b08856940ab929e
[platform/upstream/doxygen.git] / addon / doxyparse / doxyparse.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 2009-2015 by Joenio Costa.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15
16 /** @file
17  *  @brief Code parse based on doxyapp by Dimitri van Heesch
18  *
19  */
20
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include "doxygen.h"
24 #include "outputgen.h"
25 #include "parserintf.h"
26 #include "classlist.h"
27 #include "config.h"
28 #include "filedef.h"
29 #include "util.h"
30 #include "filename.h"
31 #include "arguments.h"
32 #include "memberlist.h"
33 #include "types.h"
34 #include <string>
35 #include <cstdlib>
36 #include <sstream>
37 #include <map>
38
39 class Doxyparse : public CodeOutputInterface
40 {
41   public:
42     Doxyparse(FileDef *fd) : m_fd(fd) {}
43    ~Doxyparse() {}
44
45     // these are just null functions, they can be used to produce a syntax highlighted
46     // and cross-linked version of the source code, but who needs that anyway ;-)
47     void codify(const char *) {}
48     void writeCodeLink(const char *,const char *,const char *,const char *,const char *)  {}
49     void startCodeLine() {}
50     void endCodeLine() {}
51     void startCodeAnchor(const char *) {}
52     void endCodeAnchor() {}
53     void startFontClass(const char *) {}
54     void endFontClass() {}
55     void writeCodeAnchor(const char *) {}
56     void writeLineNumber(const char *,const char *,const char *,int) {}
57     virtual void writeTooltip(const char *,const DocLinkInfo &,
58                               const char *,const char *,const SourceLinkInfo &,
59                               const SourceLinkInfo &) {}
60     void startCodeLine(bool) {}
61     void setCurrentDoc(Definition *,const char *,bool) {}
62     void addWord(const char *,bool) {}
63
64     void linkableSymbol(int l, const char *sym, Definition *symDef, Definition *context)
65     {
66       if (!symDef) {
67         // in this case we have a local or external symbol
68
69         // TODO record use of external symbols
70         // TODO must have a way to differentiate external symbols from local variables
71       }
72     }
73
74   private:
75     FileDef *m_fd;
76 };
77
78 static bool is_c_code = true;
79 static std::map<std::string, bool> modules;
80 static std::string current_module;
81
82 static void findXRefSymbols(FileDef *fd)
83 {
84   // get the interface to a parser that matches the file extension
85   ParserInterface *pIntf=Doxygen::parserManager->getParser(fd->getDefFileExtension());
86
87   // get the programming language from the file name
88   SrcLangExt lang = getLanguageFromFileName(fd->name());
89
90   // reset the parsers state
91   pIntf->resetCodeParserState();
92
93   // create a new backend object
94   Doxyparse *parse = new Doxyparse(fd);
95
96   // parse the source code
97   pIntf->parseCode(*parse, 0, fileToString(fd->absFilePath()), lang, FALSE, 0, fd);
98
99   // dismiss the object.
100   delete parse;
101 }
102
103 static bool ignoreStaticExternalCall(MemberDef *context, MemberDef *md) {
104   if (md->isStatic()) {
105     if(md->getFileDef()) {
106       if(md->getFileDef()->getOutputFileBase() == context->getFileDef()->getOutputFileBase())
107         // TODO ignore prefix of file
108         return false;
109       else
110         return true;
111     }
112     else {
113       return false;
114     }
115   }
116   else {
117     return false;
118   }
119 }
120
121 static void printFile(std::string file) {
122   printf("%s:\n", file.c_str());
123 }
124 static void printModule(std::string module) {
125   current_module = module;
126   printf("  %s:\n", module.c_str());
127 }
128 static void printClassInformation(std::string information) {
129   printf("    information: %s\n", information.c_str());
130 }
131 static void printInheritance(std::string base_class) {
132   printf("    inherits: %s\n", base_class.c_str());
133 }
134 static void printDefines() {
135   if (! modules[current_module]) {
136     printf("    defines:\n");
137   }
138   modules[current_module] = true;
139 }
140 static void printDefinition(std::string type, std::string signature, int line) {
141   printf("      - %s:\n", signature.c_str());
142   printf("          type: %s\n", type.c_str());
143   printf("          line: %d\n", line);
144 }
145 static void printProtection(std::string protection) {
146   printf("          protection: %s\n", protection.c_str());
147 }
148 static void printNumberOfLines(int lines) {
149   printf("          lines_of_code: %d\n", lines);
150 }
151 static void printNumberOfArguments(int arguments) {
152   printf("          parameters: %d\n", arguments);
153 }
154 static void printUses() {
155   printf("          uses:\n");
156 }
157 static void printReferenceTo(std::string type, std::string signature, std::string defined_in) {
158   printf("            - %s:\n", signature.c_str());
159   printf("                type: %s\n", type.c_str());
160   printf("                defined_in: %s\n", defined_in.c_str());
161 }
162 static void printNumberOfConditionalPaths(MemberDef* md) {
163   printf("          conditional_paths: %d\n", md->numberOfFlowKeyWords());
164 }
165
166 static int isPartOfCStruct(MemberDef * md) {
167   return is_c_code && md->getClassDef() != NULL;
168 }
169
170 std::string functionSignature(MemberDef* md) {
171   std::string signature = md->name().data();
172   if(md->isFunction()){
173     ArgumentList *argList = md->argumentList();
174     ArgumentListIterator iterator(*argList);
175     signature += "(";
176     Argument * argument = iterator.toFirst();
177     if(argument != NULL) {
178       signature += argument->type.data();
179       for(++iterator; (argument = iterator.current()) ;++iterator){
180         signature += std::string(",") + argument->type.data();
181       }
182     }
183     signature += ")";
184   }
185   return signature;
186 }
187
188 static void referenceTo(MemberDef* md) {
189   std::string type = md->memberTypeName().data();
190   std::string defined_in = "";
191   std::string signature = "";
192   if (isPartOfCStruct(md)) {
193     signature = md->getClassDef()->name().data() + std::string("::") + functionSignature(md);
194     defined_in = md->getClassDef()->getFileDef()->getOutputFileBase().data();
195   }
196   else {
197     signature = functionSignature(md);
198     if (md->getClassDef()) {
199       defined_in = md->getClassDef()->name().data();
200     }
201     else if (md->getFileDef()) {
202       defined_in = md->getFileDef()->getOutputFileBase().data();
203     }
204   }
205   printReferenceTo(type, signature, defined_in);
206 }
207
208 void cModule(ClassDef* cd) {
209   MemberList* ml = cd->getMemberList(MemberListType_variableMembers);
210   if (ml) {
211     MemberListIterator mli(*ml);
212     MemberDef* md;
213     for (mli.toFirst(); (md=mli.current()); ++mli) {
214       printDefinition("variable", cd->name().data() + std::string("::") + md->name().data(), md->getDefLine());
215       if (md->protection() == Public) {
216         printProtection("public");
217       }
218     }
219   }
220 }
221
222 void functionInformation(MemberDef* md) {
223   int size = md->getEndBodyLine() - md->getStartBodyLine() + 1;
224   printNumberOfLines(size);
225   ArgumentList *argList = md->argumentList();
226   printNumberOfArguments(argList->count());
227   printNumberOfConditionalPaths(md);
228   MemberSDict *defDict = md->getReferencesMembers();
229   if (defDict) {
230     MemberSDict::Iterator msdi(*defDict);
231     MemberDef *rmd;
232     printUses();
233     for (msdi.toFirst(); (rmd=msdi.current()); ++msdi) {
234       if (rmd->definitionType() == Definition::TypeMember && !ignoreStaticExternalCall(md, rmd)) {
235         referenceTo(rmd);
236       }
237     }
238   }
239 }
240
241 static void lookupSymbol(Definition *d) {
242   if (d->definitionType() == Definition::TypeMember) {
243     MemberDef *md = (MemberDef *)d;
244     std::string type = md->memberTypeName().data();
245     std::string signature = functionSignature(md);
246     printDefinition(type, signature, md->getDefLine());
247     if (md->protection() == Public) {
248       printProtection("protection public");
249     }
250     if (md->isFunction()) {
251       functionInformation(md);
252     }
253   }
254 }
255
256 void listMembers(MemberList *ml) {
257   if (ml) {
258     MemberListIterator mli(*ml);
259     MemberDef *md;
260     printDefines();
261     for (mli.toFirst(); (md=mli.current()); ++mli) {
262       lookupSymbol((Definition*) md);
263     }
264   }
265 }
266
267 void listAllMembers(ClassDef* cd) {
268   // methods
269   listMembers(cd->getMemberList(MemberListType_functionMembers));
270   // constructors
271   listMembers(cd->getMemberList(MemberListType_constructors));
272   // attributes
273   listMembers(cd->getMemberList(MemberListType_variableMembers));
274 }
275
276 static void classInformation(ClassDef* cd) {
277   if (is_c_code) {
278     cModule(cd);
279   } else {
280     printModule(cd->name().data());
281     BaseClassList* baseClasses = cd->baseClasses();
282     if (baseClasses) {
283       BaseClassListIterator bci(*baseClasses);
284       BaseClassDef* bcd;
285       for (bci.toFirst(); (bcd = bci.current()); ++bci) {
286         printInheritance(bcd->classDef->name().data());
287       }
288     }
289     if(cd->isAbstract()) {
290       printClassInformation("abstract class");
291     }
292     listAllMembers(cd);
293   }
294 }
295
296 static bool checkLanguage(std::string& filename, std::string extension) {
297   if (filename.find(extension, filename.size() - extension.size()) != std::string::npos) {
298     return true;
299   } else {
300     return false;
301   }
302 }
303
304 /* Detects the programming language of the project. Actually, we only care
305  * about whether it is a C project or not. */
306 static void detectProgrammingLanguage(FileNameListIterator& fnli) {
307   FileName* fn;
308   for (fnli.toFirst(); (fn=fnli.current()); ++fnli) {
309     std::string filename = fn->fileName();
310     if (
311         checkLanguage(filename, ".cc") ||
312         checkLanguage(filename, ".cxx") ||
313         checkLanguage(filename, ".cpp") ||
314         checkLanguage(filename, ".java")
315        ) {
316       is_c_code = false;
317     }
318   }
319 }
320
321 static void listSymbols() {
322   // iterate over the input files
323   FileNameListIterator fnli(*Doxygen::inputNameList);
324   FileName *fn;
325
326   detectProgrammingLanguage(fnli);
327
328   // for each file
329   for (fnli.toFirst(); (fn=fnli.current()); ++fnli) {
330     FileNameIterator fni(*fn);
331     FileDef *fd;
332     for (; (fd=fni.current()); ++fni) {
333       printFile(fd->absFilePath().data());
334       MemberList *ml = fd->getMemberList(MemberListType_allMembersList);
335       if (ml && ml->count() > 0) {
336         printModule(fd->getOutputFileBase().data());
337         listMembers(ml);
338       }
339
340       ClassSDict *classes = fd->getClassSDict();
341       if (classes) {
342         ClassSDict::Iterator cli(*classes);
343         ClassDef *cd;
344         for (cli.toFirst(); (cd = cli.current()); ++cli) {
345           classInformation(cd);
346         }
347       }
348     }
349   }
350   // TODO print external symbols referenced
351 }
352
353 int main(int argc,char **argv) {
354   if (argc < 2) {
355     printf("Usage: %s [source_file | source_dir]\n",argv[0]);
356     exit(1);
357   }
358
359   // initialize data structures
360   initDoxygen();
361
362   // check and finalize the configuration
363   checkConfiguration();
364   adjustConfiguration();
365
366   // setup the non-default configuration options
367
368   // we need a place to put intermediate files
369   std::ostringstream tmpdir;
370   tmpdir << "/tmp/doxyparse-" << getpid();
371   Config_getString(OUTPUT_DIRECTORY)= tmpdir.str().c_str();
372   // enable HTML (fake) output to omit warning about missing output format
373   Config_getBool(GENERATE_HTML)=TRUE;
374   // disable latex output
375   Config_getBool(GENERATE_LATEX)=FALSE;
376   // be quiet
377   Config_getBool(QUIET)=TRUE;
378   // turn off warnings
379   Config_getBool(WARNINGS)=FALSE;
380   Config_getBool(WARN_IF_UNDOCUMENTED)=FALSE;
381   Config_getBool(WARN_IF_DOC_ERROR)=FALSE;
382   // Extract as much as possible
383   Config_getBool(EXTRACT_ALL)=TRUE;
384   Config_getBool(EXTRACT_STATIC)=TRUE;
385   Config_getBool(EXTRACT_PRIVATE)=TRUE;
386   Config_getBool(EXTRACT_LOCAL_METHODS)=TRUE;
387   // Extract source browse information, needed
388   // to make doxygen gather the cross reference info
389   Config_getBool(SOURCE_BROWSER)=TRUE;
390   // find functions call between modules
391   Config_getBool(CALL_GRAPH)=TRUE;
392   // loop recursive over input files
393   Config_getBool(RECURSIVE)=TRUE;
394   // set the input
395   Config_getList(INPUT).clear();
396   for (int i = 1; i < argc; i++) {
397     if (strcmp(argv[i], "-") == 0) {
398       char filename[1024];
399       while (1) {
400         scanf("%s[^\n]", filename);
401         if (feof(stdin)) {
402           break;
403         }
404         Config_getList(INPUT).append(filename);
405       }
406     } else {
407       Config_getList(INPUT).append(argv[i]);
408     }
409   }
410   if (Config_getList(INPUT).isEmpty()) {
411     exit(0);
412   }
413
414   // parse the files
415   parseInput();
416
417   // iterate over the input files
418   FileNameListIterator fnli(*Doxygen::inputNameList);
419   FileName *fn;
420   // for each file with a certain name
421   for (fnli.toFirst();(fn=fnli.current());++fnli) {
422     FileNameIterator fni(*fn);
423     FileDef *fd;
424     // for each file definition
425     for (;(fd=fni.current());++fni) {
426       // get the references (linked and unlinked) found in this file
427       findXRefSymbols(fd);
428     }
429   }
430
431   // remove temporary files
432   if (!Doxygen::objDBFileName.isEmpty()) unlink(Doxygen::objDBFileName);
433   if (!Doxygen::entryDBFileName.isEmpty()) unlink(Doxygen::entryDBFileName);
434   // clean up after us
435   rmdir(Config_getString(OUTPUT_DIRECTORY));
436
437   listSymbols();
438
439   std::string cleanup_command = "rm -rf ";
440   cleanup_command += tmpdir.str();
441   system(cleanup_command.c_str());
442
443   exit(0);
444 }