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 #if !defined(_WIN32) || defined(__CYGWIN__)
23 #include <unistd.h>
24 #else
25 #include <windows.h>
26 #endif
27 #include "version.h"
28 #include "doxygen.h"
29 #include "outputgen.h"
30 #include "parserintf.h"
31 #include "classlist.h"
32 #include "config.h"
33 #include "filedef.h"
34 #include "util.h"
35 #include "filename.h"
36 #include "arguments.h"
37 #include "memberlist.h"
38 #include "types.h"
39 #include <string>
40 #include <cstdlib>
41 #include <sstream>
42 #include <map>
43 #include "qcstring.h"
44 #include "namespacedef.h"
45 #include "portable.h"
46 #include "dir.h"
47
48 class Doxyparse : public CodeOutputInterface
49 {
50 public:
Doxyparse(const FileDef * fd)51 Doxyparse(const FileDef *fd) : m_fd(fd) {}
~Doxyparse()52 ~Doxyparse() {}
53
54 // these are just null functions, they can be used to produce a syntax highlighted
55 // and cross-linked version of the source code, but who needs that anyway ;-)
codify(const QCString &)56 void codify(const QCString &) override {}
writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &)57 void writeCodeLink(CodeSymbolType,const QCString &,const QCString &,const QCString &,const QCString &,const QCString &) override {}
startCodeLine(bool)58 void startCodeLine(bool) override {}
endCodeLine()59 void endCodeLine() override {}
writeCodeAnchor(const QCString &)60 void writeCodeAnchor(const QCString &) override {}
startFontClass(const QCString &)61 void startFontClass(const QCString &) override {}
endFontClass()62 void endFontClass() override {}
writeLineNumber(const QCString &,const QCString &,const QCString &,int,bool)63 void writeLineNumber(const QCString &,const QCString &,const QCString &,int,bool) override {}
writeTooltip(const QCString &,const DocLinkInfo &,const QCString &,const QCString &,const SourceLinkInfo &,const SourceLinkInfo &)64 virtual void writeTooltip(const QCString &,const DocLinkInfo &,
65 const QCString &,const QCString &,const SourceLinkInfo &,
66 const SourceLinkInfo &) override {}
setCurrentDoc(const Definition *,const QCString &,bool)67 void setCurrentDoc(const Definition *,const QCString &,bool) override {}
addWord(const QCString &,bool)68 void addWord(const QCString &,bool) override {}
startCodeFragment(const QCString &)69 void startCodeFragment(const QCString &) override {}
endCodeFragment(const QCString &)70 void endCodeFragment(const QCString &) override {}
71
linkableSymbol(int l,const char * sym,Definition * symDef,Definition * context)72 void linkableSymbol(int l, const char *sym, Definition *symDef, Definition *context)
73 {
74 if (!symDef) {
75 // in this case we have a local or external symbol
76
77 // TODO record use of external symbols
78 // TODO must have a way to differentiate external symbols from local variables
79 }
80 }
81
82 private:
83 const FileDef *m_fd;
84 };
85
86 static bool is_c_code = true;
87
findXRefSymbols(FileDef * fd)88 static void findXRefSymbols(FileDef *fd)
89 {
90 // get the interface to a parser that matches the file extension
91 auto intf=Doxygen::parserManager->getCodeParser(fd->getDefFileExtension());
92
93 // get the programming language from the file name
94 SrcLangExt lang = getLanguageFromFileName(fd->name());
95
96 // reset the parsers state
97 intf->resetCodeParserState();
98
99 // create a new backend object
100 Doxyparse *parse = new Doxyparse(fd);
101
102 // parse the source code
103 intf->parseCode(*parse, 0, fileToString(fd->absFilePath()), lang, FALSE, 0, fd);
104
105 // dismiss the object.
106 delete parse;
107 }
108
ignoreStaticExternalCall(const MemberDef * context,const MemberDef * md)109 static bool ignoreStaticExternalCall(const MemberDef *context, const MemberDef *md) {
110 if (md->isStatic()) {
111 if(md->getFileDef() && context->getFileDef()) {
112 if(md->getFileDef()->getOutputFileBase() == context->getFileDef()->getOutputFileBase())
113 // TODO ignore prefix of file
114 return false;
115 else
116 return true;
117 }
118 else {
119 return false;
120 }
121 }
122 else {
123 return false;
124 }
125 }
126
startYamlDocument()127 static void startYamlDocument() {
128 printf("---\n");
129 }
printFile(std::string file)130 static void printFile(std::string file) {
131 printf("%s:\n", file.c_str());
132 }
printModule(std::string module)133 static void printModule(std::string module) {
134 printf(" \"%s\":\n", unescapeCharsInString(module.c_str()).data());
135 }
printClassInformation(std::string information)136 static void printClassInformation(std::string information) {
137 printf(" information: %s\n", information.c_str());
138 }
printInherits()139 static void printInherits() {
140 printf(" inherits:\n");
141 }
printInheritance(std::string base_class)142 static void printInheritance(std::string base_class) {
143 printf(" - \"%s\"\n", base_class.c_str());
144 }
printDefines()145 static void printDefines() {
146 printf(" defines:\n");
147 }
printDefinition(std::string type,std::string signature,int line)148 static void printDefinition(std::string type, std::string signature, int line) {
149 printf(" - \"%s\":\n", signature.substr(0, 1022).c_str());
150 printf(" type: %s\n", type.c_str());
151 printf(" line: %d\n", line);
152 }
printProtection(std::string protection)153 static void printProtection(std::string protection) {
154 printf(" protection: %s\n", protection.c_str());
155 }
printPrototypeYes()156 static void printPrototypeYes() {
157 printf(" prototype: yes\n");
158 }
printNumberOfLines(int lines)159 static void printNumberOfLines(int lines) {
160 printf(" lines_of_code: %d\n", lines);
161 }
printNumberOfArguments(size_t arguments)162 static void printNumberOfArguments(size_t arguments) {
163 printf(" parameters: %zu\n", arguments);
164 }
printUses()165 static void printUses() {
166 printf(" uses:\n");
167 }
printReferenceTo(std::string type,std::string signature,std::string defined_in)168 static void printReferenceTo(std::string type, std::string signature, std::string defined_in) {
169 printf(" - \"%s\":\n", signature.substr(0, 1022).c_str());
170 printf(" type: %s\n", type.c_str());
171 printf(" defined_in: \"%s\"\n", unescapeCharsInString(defined_in.c_str()).data());
172 }
printNumberOfConditionalPaths(const MemberDef * md)173 static void printNumberOfConditionalPaths(const MemberDef* md) {
174 printf(" conditional_paths: %d\n", md->numberOfFlowKeyWords());
175 }
176
isPartOfCStruct(const MemberDef * md)177 static int isPartOfCStruct(const MemberDef * md) {
178 return is_c_code && md->getClassDef() != NULL;
179 }
180
sanitizeString(std::string data)181 std::string sanitizeString(std::string data) {
182 QCString new_data = QCString(data.c_str());
183 new_data = substitute(new_data,"\"", "");
184 new_data = substitute(new_data,"\'", ""); // https://github.com/analizo/analizo/issues/138
185 return !new_data.isEmpty() ? new_data.data() : "";
186 }
187
argumentData(const Argument & argument)188 std::string argumentData(const Argument &argument) {
189 std::string data = "";
190 if (argument.type.size() > 1)
191 data = sanitizeString(argument.type.data());
192 else if (!argument.name.isEmpty())
193 data = sanitizeString(argument.name.data());
194 return data;
195 }
196
functionSignature(const MemberDef * md)197 std::string functionSignature(const MemberDef* md) {
198 std::string signature = sanitizeString(md->name().data());
199 if(md->isFunction()){
200 const ArgumentList &argList = md->argumentList();
201 signature += "(";
202 auto it = argList.begin();
203 if(it!=argList.end()) {
204 signature += argumentData(*it);
205 for(++it; it!=argList.end(); ++it) {
206 signature += std::string(",") + argumentData(*it);
207 }
208 }
209 signature += ")";
210 }
211 return signature;
212 }
213
referenceTo(const MemberDef * md)214 static void referenceTo(const MemberDef* md) {
215 std::string type = md->memberTypeName().data();
216 std::string defined_in = "";
217 std::string signature = "";
218 if (isPartOfCStruct(md)) {
219 signature = md->getClassDef()->name().data() + std::string("::") + functionSignature(md);
220 defined_in = md->getClassDef()->getFileDef()->getOutputFileBase().data();
221 }
222 else {
223 signature = functionSignature(md);
224 if (md->getClassDef()) {
225 defined_in = md->getClassDef()->name().data();
226 }
227 else if (md->getFileDef()) {
228 defined_in = md->getFileDef()->getOutputFileBase().data();
229 }
230 else if (md->getNamespaceDef()) {
231 defined_in = md->getNamespaceDef()->name().data();
232 }
233 }
234 printReferenceTo(type, signature, defined_in);
235 }
236
protectionInformation(Protection protection)237 void protectionInformation(Protection protection) {
238 if (protection == Public) {
239 printProtection("public");
240 }
241 else if (protection == Protected) {
242 printProtection("protected");
243 }
244 else if (protection == Private) {
245 printProtection("private");
246 }
247 else if (protection == Package) {
248 printProtection("package");
249 }
250 }
251
cModule(const ClassDef * cd)252 void cModule(const ClassDef* cd) {
253 const MemberList* ml = cd->getMemberList(MemberListType_variableMembers);
254 if (ml) {
255 const FileDef *fd = cd->getFileDef();
256 const MemberList *fd_ml = fd->getMemberList(MemberListType_allMembersList);
257 if (!fd_ml || fd_ml->size() == 0) {
258 printModule(fd->getOutputFileBase().data());
259 printDefines();
260 }
261 for (const auto &md : *ml) {
262 printDefinition("variable", cd->name().data() + std::string("::") + md->name().data(), md->getDefLine());
263 protectionInformation(md->protection());
264 }
265 }
266 }
267
checkOverrideArg(const ArgumentList & argList,const MemberDef * md)268 static bool checkOverrideArg(const ArgumentList &argList, const MemberDef *md) {
269 if(!md->isFunction() || argList.empty()){
270 return false;
271 }
272
273 for (const Argument &argument : argList) {
274 if(md->name() == argument.name) {
275 return true;
276 }
277 }
278
279 return false;
280 }
281
functionInformation(const MemberDef * md)282 void functionInformation(const MemberDef* md) {
283 std::string temp = "";
284 int size = md->getEndBodyLine() - md->getStartBodyLine() + 1;
285 printNumberOfLines(size);
286 const ArgumentList &argList = md->argumentList();
287 if (!argList.empty())
288 {
289 temp = argumentData(argList.front());
290 // TODO: This is a workaround; better not include "void" in argList, in the first place.
291 if (temp!="void")
292 {
293 printNumberOfArguments(argList.size());
294 }
295 }
296
297 printNumberOfConditionalPaths(md);
298 auto refList = md->getReferencesMembers();
299 if (!refList.empty()) {
300 printUses();
301 for (const auto &rmd : refList) {
302 if (rmd->definitionType() == Definition::TypeMember && !ignoreStaticExternalCall(md, rmd) && !checkOverrideArg(argList, rmd)) {
303 referenceTo(rmd);
304 }
305 }
306 }
307 }
308
prototypeInformation(const MemberDef * md)309 void prototypeInformation(const MemberDef* md) {
310 printPrototypeYes();
311 const ArgumentList &argList = md->argumentList();
312 printNumberOfArguments(argList.size());
313 }
314
lookupSymbol(const Definition * d)315 static void lookupSymbol(const Definition *d) {
316 if (d->definitionType() == Definition::TypeMember) {
317 const MemberDef *md = dynamic_cast<const MemberDef*>(d);
318 std::string type = md->memberTypeName().data();
319 std::string signature = functionSignature(md);
320 printDefinition(type, signature, md->getDefLine());
321 protectionInformation(md->protection());
322 if (md->isFunction() && md->isPrototype()) {
323 prototypeInformation(md);
324 }
325 else if (md->isFunction()) {
326 functionInformation(md);
327 }
328 }
329 }
330
listMembers(const MemberList * ml)331 void listMembers(const MemberList *ml) {
332 if (ml) {
333 for (const auto &md : *ml) {
334 lookupSymbol((Definition*) md);
335 }
336 }
337 }
338
listAllMembers(const ClassDef * cd)339 void listAllMembers(const ClassDef* cd) {
340 // methods
341 listMembers(cd->getMemberList(MemberListType_functionMembers));
342 // constructors
343 listMembers(cd->getMemberList(MemberListType_constructors));
344 // attributes
345 listMembers(cd->getMemberList(MemberListType_variableMembers));
346 }
347
classInformation(const ClassDef * cd)348 static void classInformation(const ClassDef* cd) {
349 if (is_c_code) {
350 cModule(cd);
351 } else {
352 printModule(cd->name().data());
353 if (!cd->baseClasses().empty()) {
354 printInherits();
355 for (const auto &bcd : cd->baseClasses()) {
356 printInheritance(sanitizeString(bcd.classDef->name().data()));
357 }
358 }
359 if(cd->isAbstract()) {
360 printClassInformation("abstract class");
361 }
362 printDefines();
363 listAllMembers(cd);
364 }
365 }
366
checkLanguage(std::string & filename,std::string extension)367 static bool checkLanguage(std::string& filename, std::string extension) {
368 if (filename.find(extension, filename.size() - extension.size()) != std::string::npos) {
369 return true;
370 } else {
371 return false;
372 }
373 }
374
375 /* Detects the programming language of the project. Actually, we only care
376 * about whether it is a C project or not. */
detectProgrammingLanguage(FileNameLinkedMap & fnli)377 static void detectProgrammingLanguage(FileNameLinkedMap &fnli) {
378 for (const auto &fn : fnli) {
379 std::string filename = fn->fileName().str();
380 if (
381 checkLanguage(filename, ".cc") ||
382 checkLanguage(filename, ".cxx") ||
383 checkLanguage(filename, ".cpp") ||
384 checkLanguage(filename, ".java") ||
385 checkLanguage(filename, ".py") ||
386 checkLanguage(filename, ".pyw") ||
387 checkLanguage(filename, ".cs")
388 ) {
389 is_c_code = false;
390 }
391 }
392 }
393
listSymbols()394 static void listSymbols() {
395 detectProgrammingLanguage(*Doxygen::inputNameLinkedMap);
396
397 // iterate over the input files
398 for (const auto &fn : *Doxygen::inputNameLinkedMap) {
399 for (const auto &fd : *fn) {
400 printFile(fd->absFilePath().data());
401 MemberList *ml = fd->getMemberList(MemberListType_allMembersList);
402 if (ml && ml->size() > 0) {
403 printModule(fd->getOutputFileBase().data());
404 printDefines();
405 listMembers(ml);
406 }
407
408 ClassDefSet visitedClasses;
409 for (const auto &cd : fd->getClasses()) {
410 if (visitedClasses.find(cd)==visitedClasses.end()) {
411 classInformation(cd);
412 visitedClasses.insert(cd);
413 }
414 }
415 }
416 }
417 // TODO print external symbols referenced
418 }
419
main(int argc,char ** argv)420 int main(int argc,char **argv) {
421 int locArgc = argc;
422
423 if (locArgc == 2)
424 {
425 if (!strcmp(argv[1],"--help"))
426 {
427 printf("Usage: %s [source_file | source_dir]\n",argv[0]);
428 exit(0);
429 }
430 else if (!strcmp(argv[1],"--version"))
431 {
432 printf("%s version: %s\n",argv[0],getFullVersion());
433 exit(0);
434 }
435 }
436
437 if (locArgc!=2)
438 {
439 printf("Usage: %s [source_file | source_dir]\n",argv[0]);
440 exit(1);
441 }
442
443 // initialize data structures
444 initDoxygen();
445
446 // check and finalize the configuration
447 checkConfiguration();
448 adjustConfiguration();
449
450 // setup the non-default configuration options
451
452 // we need a place to put intermediate files
453 std::ostringstream tmpdir;
454 unsigned int pid = Portable::pid();
455 if (!Portable::getenv("TMP").isEmpty())
456 tmpdir << Portable::getenv("TMP") << "/doxyparse-" << pid;
457 else if (!Portable::getenv("TEMP").isEmpty())
458 tmpdir << Portable::getenv("TEMP") << "/doxyparse-" << pid;
459 else
460 tmpdir << "doxyparse-" << pid;
461
462 Config_updateString(OUTPUT_DIRECTORY,tmpdir.str().c_str());
463 // enable HTML (fake) output to omit warning about missing output format
464 Config_updateBool(GENERATE_HTML,TRUE);
465 // disable latex output
466 Config_updateBool(GENERATE_LATEX,FALSE);
467 // be quiet
468 Config_updateBool(QUIET,TRUE);
469 // turn off warnings
470 Config_updateBool(WARNINGS,FALSE);
471 Config_updateBool(WARN_IF_UNDOCUMENTED,FALSE);
472 Config_updateBool(WARN_IF_DOC_ERROR,FALSE);
473 // Extract as much as possible
474 Config_updateBool(EXTRACT_ALL,TRUE);
475 Config_updateBool(EXTRACT_STATIC,TRUE);
476 Config_updateBool(EXTRACT_PRIVATE,TRUE);
477 Config_updateBool(EXTRACT_LOCAL_METHODS,TRUE);
478 Config_updateBool(EXTRACT_PACKAGE,TRUE);
479 // Extract source browse information, needed
480 // to make doxygen gather the cross reference info
481 Config_updateBool(SOURCE_BROWSER,TRUE);
482 // find functions call between modules
483 Config_updateBool(CALL_GRAPH,TRUE);
484 // loop recursive over input files
485 Config_updateBool(RECURSIVE,TRUE);
486 // add file extensions
487 Config_updateList(FILE_PATTERNS, { "*.cc", "*.cxx", "*.cpp", "*.java",
488 "*.py", "*.pyw", "*.cs", "*.c", "*.h", "*.hh", "*.hpp"});
489 // set the input
490 StringVector inputList;
491 for (int i = 1; i < argc; i++) {
492 if (strcmp(argv[i], "-") == 0) {
493 char filename[1024];
494 while (1) {
495 scanf("%s[^\n]", filename);
496 if (feof(stdin)) {
497 break;
498 }
499 inputList.push_back(filename);
500 }
501 } else {
502 inputList.push_back(argv[i]);
503 }
504 }
505 Config_updateList(INPUT,inputList);
506 if (inputList.empty()) {
507 exit(0);
508 }
509
510 // parse the files
511 parseInput();
512
513 // iterate over the input files
514 for (const auto &fn : *Doxygen::inputNameLinkedMap) {
515 for (const auto &fd : *fn) {
516 // get the references (linked and unlinked) found in this file
517 findXRefSymbols(fd.get());
518 }
519 }
520
521 Dir thisDir;
522 // remove temporary files
523 if (!Doxygen::filterDBFileName.isEmpty()) thisDir.remove(Doxygen::filterDBFileName.str());
524
525 // clean up after us
526 thisDir.rmdir(Config_getString(OUTPUT_DIRECTORY).str());
527
528 startYamlDocument();
529 listSymbols();
530
531 std::string cleanup_command = "rm -rf ";
532 cleanup_command += tmpdir.str();
533 system(cleanup_command.c_str());
534
535 exit(0);
536 }
537