Imported Upstream version 1.8.15
[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 removeDoubleQuotes(std::string data) {
171   // remove surrounding double quotes
172   if (data.front() == '"' && data.back() == '"') {
173     data.erase(0, 1);            // first double quote
174     data.erase(data.size() - 1); // last double quote
175   }
176   return data;
177 }
178
179 std::string argumentData(Argument *argument) {
180   std::string data = "";
181   if (argument->type != NULL)
182     data = removeDoubleQuotes(argument->type.data());
183   else if (argument->name != NULL)
184     data = removeDoubleQuotes(argument->name.data());
185   return data;
186 }
187
188 std::string functionSignature(MemberDef* md) {
189   std::string signature = md->name().data();
190   if(md->isFunction()){
191     ArgumentList *argList = md->argumentList();
192     ArgumentListIterator iterator(*argList);
193     signature += "(";
194     Argument * argument = iterator.toFirst();
195     if(argument != NULL) {
196       signature += argumentData(argument);
197       for(++iterator; (argument = iterator.current()); ++iterator){
198         signature += std::string(",") + argumentData(argument);
199       }
200     }
201     signature += ")";
202   }
203   return signature;
204 }
205
206 static void referenceTo(MemberDef* md) {
207   std::string type = md->memberTypeName().data();
208   std::string defined_in = "";
209   std::string signature = "";
210   if (isPartOfCStruct(md)) {
211     signature = md->getClassDef()->name().data() + std::string("::") + functionSignature(md);
212     defined_in = md->getClassDef()->getFileDef()->getOutputFileBase().data();
213   }
214   else {
215     signature = functionSignature(md);
216     if (md->getClassDef()) {
217       defined_in = md->getClassDef()->name().data();
218     }
219     else if (md->getFileDef()) {
220       defined_in = md->getFileDef()->getOutputFileBase().data();
221     }
222   }
223   printReferenceTo(type, signature, defined_in);
224 }
225
226 void cModule(ClassDef* cd) {
227   MemberList* ml = cd->getMemberList(MemberListType_variableMembers);
228   if (ml) {
229     MemberListIterator mli(*ml);
230     MemberDef* md;
231     for (mli.toFirst(); (md=mli.current()); ++mli) {
232       printDefinition("variable", cd->name().data() + std::string("::") + md->name().data(), md->getDefLine());
233       if (md->protection() == Public) {
234         printProtection("public");
235       }
236     }
237   }
238 }
239
240 void functionInformation(MemberDef* md) {
241   int size = md->getEndBodyLine() - md->getStartBodyLine() + 1;
242   printNumberOfLines(size);
243   ArgumentList *argList = md->argumentList();
244   printNumberOfArguments(argList->count());
245   printNumberOfConditionalPaths(md);
246   MemberSDict *defDict = md->getReferencesMembers();
247   if (defDict) {
248     MemberSDict::Iterator msdi(*defDict);
249     MemberDef *rmd;
250     printUses();
251     for (msdi.toFirst(); (rmd=msdi.current()); ++msdi) {
252       if (rmd->definitionType() == Definition::TypeMember && !ignoreStaticExternalCall(md, rmd)) {
253         referenceTo(rmd);
254       }
255     }
256   }
257 }
258
259 static void lookupSymbol(Definition *d) {
260   if (d->definitionType() == Definition::TypeMember) {
261     MemberDef *md = (MemberDef *)d;
262     std::string type = md->memberTypeName().data();
263     std::string signature = functionSignature(md);
264     printDefinition(type, signature, md->getDefLine());
265     if (md->protection() == Public) {
266       printProtection("public");
267     }
268     if (md->isFunction()) {
269       functionInformation(md);
270     }
271   }
272 }
273
274 void listMembers(MemberList *ml) {
275   if (ml) {
276     MemberListIterator mli(*ml);
277     MemberDef *md;
278     printDefines();
279     for (mli.toFirst(); (md=mli.current()); ++mli) {
280       lookupSymbol((Definition*) md);
281     }
282   }
283 }
284
285 void listAllMembers(ClassDef* cd) {
286   // methods
287   listMembers(cd->getMemberList(MemberListType_functionMembers));
288   // constructors
289   listMembers(cd->getMemberList(MemberListType_constructors));
290   // attributes
291   listMembers(cd->getMemberList(MemberListType_variableMembers));
292 }
293
294 static void classInformation(ClassDef* cd) {
295   if (is_c_code) {
296     cModule(cd);
297   } else {
298     printModule(cd->name().data());
299     BaseClassList* baseClasses = cd->baseClasses();
300     if (baseClasses) {
301       BaseClassListIterator bci(*baseClasses);
302       BaseClassDef* bcd;
303       for (bci.toFirst(); (bcd = bci.current()); ++bci) {
304         printInheritance(bcd->classDef->name().data());
305       }
306     }
307     if(cd->isAbstract()) {
308       printClassInformation("abstract class");
309     }
310     listAllMembers(cd);
311   }
312 }
313
314 static bool checkLanguage(std::string& filename, std::string extension) {
315   if (filename.find(extension, filename.size() - extension.size()) != std::string::npos) {
316     return true;
317   } else {
318     return false;
319   }
320 }
321
322 /* Detects the programming language of the project. Actually, we only care
323  * about whether it is a C project or not. */
324 static void detectProgrammingLanguage(FileNameListIterator& fnli) {
325   FileName* fn;
326   for (fnli.toFirst(); (fn=fnli.current()); ++fnli) {
327     std::string filename = fn->fileName();
328     if (
329         checkLanguage(filename, ".cc") ||
330         checkLanguage(filename, ".cxx") ||
331         checkLanguage(filename, ".cpp") ||
332         checkLanguage(filename, ".java") ||
333         checkLanguage(filename, ".py") ||
334         checkLanguage(filename, ".pyw") ||
335         checkLanguage(filename, ".cs")
336        ) {
337       is_c_code = false;
338     }
339   }
340 }
341
342 static void listSymbols() {
343   // iterate over the input files
344   FileNameListIterator fnli(*Doxygen::inputNameList);
345   FileName *fn;
346
347   detectProgrammingLanguage(fnli);
348
349   // for each file
350   for (fnli.toFirst(); (fn=fnli.current()); ++fnli) {
351     FileNameIterator fni(*fn);
352     FileDef *fd;
353     for (; (fd=fni.current()); ++fni) {
354       printFile(fd->absFilePath().data());
355       MemberList *ml = fd->getMemberList(MemberListType_allMembersList);
356       if (ml && ml->count() > 0) {
357         printModule(fd->getOutputFileBase().data());
358         listMembers(ml);
359       }
360
361       ClassSDict *classes = fd->getClassSDict();
362       if (classes) {
363         ClassSDict::Iterator cli(*classes);
364         ClassDef *cd;
365         for (cli.toFirst(); (cd = cli.current()); ++cli) {
366           classInformation(cd);
367         }
368       }
369     }
370   }
371   // TODO print external symbols referenced
372 }
373
374 int main(int argc,char **argv) {
375   if (argc < 2) {
376     printf("Usage: %s [source_file | source_dir]\n",argv[0]);
377     exit(1);
378   }
379
380   // initialize data structures
381   initDoxygen();
382
383   // check and finalize the configuration
384   checkConfiguration();
385   adjustConfiguration();
386
387   // setup the non-default configuration options
388
389   // we need a place to put intermediate files
390   std::ostringstream tmpdir;
391   tmpdir << "/tmp/doxyparse-" << getpid();
392   Config_getString(OUTPUT_DIRECTORY)= tmpdir.str().c_str();
393   // enable HTML (fake) output to omit warning about missing output format
394   Config_getBool(GENERATE_HTML)=TRUE;
395   // disable latex output
396   Config_getBool(GENERATE_LATEX)=FALSE;
397   // be quiet
398   Config_getBool(QUIET)=TRUE;
399   // turn off warnings
400   Config_getBool(WARNINGS)=FALSE;
401   Config_getBool(WARN_IF_UNDOCUMENTED)=FALSE;
402   Config_getBool(WARN_IF_DOC_ERROR)=FALSE;
403   // Extract as much as possible
404   Config_getBool(EXTRACT_ALL)=TRUE;
405   Config_getBool(EXTRACT_STATIC)=TRUE;
406   Config_getBool(EXTRACT_PRIVATE)=TRUE;
407   Config_getBool(EXTRACT_LOCAL_METHODS)=TRUE;
408   // Extract source browse information, needed
409   // to make doxygen gather the cross reference info
410   Config_getBool(SOURCE_BROWSER)=TRUE;
411   // find functions call between modules
412   Config_getBool(CALL_GRAPH)=TRUE;
413   // loop recursive over input files
414   Config_getBool(RECURSIVE)=TRUE;
415   // set the input
416   Config_getList(INPUT).clear();
417   for (int i = 1; i < argc; i++) {
418     if (strcmp(argv[i], "-") == 0) {
419       char filename[1024];
420       while (1) {
421         scanf("%s[^\n]", filename);
422         if (feof(stdin)) {
423           break;
424         }
425         Config_getList(INPUT).append(filename);
426       }
427     } else {
428       Config_getList(INPUT).append(argv[i]);
429     }
430   }
431   if (Config_getList(INPUT).isEmpty()) {
432     exit(0);
433   }
434
435   // parse the files
436   parseInput();
437
438   // iterate over the input files
439   FileNameListIterator fnli(*Doxygen::inputNameList);
440   FileName *fn;
441   // for each file with a certain name
442   for (fnli.toFirst();(fn=fnli.current());++fnli) {
443     FileNameIterator fni(*fn);
444     FileDef *fd;
445     // for each file definition
446     for (;(fd=fni.current());++fni) {
447       // get the references (linked and unlinked) found in this file
448       findXRefSymbols(fd);
449     }
450   }
451
452   // remove temporary files
453   if (!Doxygen::objDBFileName.isEmpty()) unlink(Doxygen::objDBFileName);
454   if (!Doxygen::entryDBFileName.isEmpty()) unlink(Doxygen::entryDBFileName);
455   // clean up after us
456   rmdir(Config_getString(OUTPUT_DIRECTORY));
457
458   listSymbols();
459
460   std::string cleanup_command = "rm -rf ";
461   cleanup_command += tmpdir.str();
462   system(cleanup_command.c_str());
463
464   exit(0);
465 }