2 ////////////////////////////////////////////////////////////////////////////////////////
4 // This file is part of Boost Statechart Viewer.
6 // Boost Statechart Viewer is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // Boost Statechart Viewer is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with Boost Statechart Viewer. If not, see <http://www.gnu.org/licenses/>.
19 ////////////////////////////////////////////////////////////////////////////////////////
21 //standard header files
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/Support/Host.h"
27 #include "llvm/Config/config.h"
30 #include "clang/Frontend/TextDiagnosticPrinter.h"
31 #include "clang/Lex/HeaderSearch.h"
32 #include "clang/Basic/FileManager.h"
33 #include "clang/Frontend/Utils.h"
34 #include "clang/Basic/TargetInfo.h"
35 #include "clang/Lex/Preprocessor.h"
36 #include "clang/Frontend/CompilerInstance.h"
37 #include "clang/AST/ASTConsumer.h"
38 #include "clang/Sema/Lookup.h"
39 #include "clang/Parse/ParseAST.h"
40 #include "clang/Basic/Version.h"
41 #include "clang/Driver/Driver.h"
42 #include "clang/Driver/Compilation.h"
47 using namespace clang;
48 using namespace clang::driver;
52 * This class provides Simple diagnostic Client. It uses implementation in library for printing diagnostci information.
53 * Also it counts number of warnings, errors, ... When an error occurs the program is stopped.
55 class MyDiagnosticClient : public TextDiagnosticPrinter
57 int nwarnings; /** Save number of Warnings occured during diagnostic */
63 * Initialize number of warnings, errors, ...
65 MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false)
73 * This method prints diagnostic and counts diagnostic types.
75 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
77 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information using library implementation
78 switch (DiagLevel) // count number of all diagnostic information
80 case 0 : nignored+=1; break;
81 case 1 : nnotes+=1; break;
82 case 2 : nwarnings+=1; break;
89 * Print statistics about diagnostic.
93 cout<<"\n--Diagnostic Info--\n";
94 cout<<"Number of ignored: "<<nignored<<"\n";
95 cout<<"Number of notes: "<<nnotes<<"\n";
96 cout<<"Number of warnings: "<<nwarnings<<"\n";
97 cout<<"Number of errors and fatal errors: "<<nerrors<<"\n";
100 int getNbrOfWarnings() /** Return number of warnings */
105 int getNbrOfNotes() /** Return number of notes */
110 int getNbrOfIgnored() /** Return number of ignored */
117 * My ASTConsumer provides interface for traversing AST. It uses recursive traversing in namespaces.
119 class FindStates : public ASTConsumer
121 list<string> transitions;
122 list<string> cReactions; /** list of custom reactions. After all files are traversed this list should be empty. */
125 string name_of_machine;
126 string name_of_start;
127 FullSourceLoc *fsloc; /** Full Source Location instance for holding Source Manager. */
130 list<string> getStates() /** Return list of states of the state machine. */
135 list<string> getTransitions() /** Return list of transitions. */
140 list<string> getEvents() /** Return list of events. */
145 string getStateMachine() /** Return name of the state machine. */
147 return name_of_machine;
150 string getNameOfFirstState() /** Return name of start state. */
152 return name_of_start;
155 virtual void Initialize(ASTContext &ctx)/** Run after the AST is constructed before the consumer starts to work. So this function works like constructor. */
157 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
159 name_of_machine = "";
163 * Traverse global decls using DeclGroupRef for handling all global decls. But only interesting decls are processed. Interesting decls are Struct, Class, C++ methods and Namespace.
164 * When Namespace is found it recursively traverse all decls inside this Namespace using method recursive_visit.
166 virtual bool HandleTopLevelDecl(DeclGroupRef DGR)
169 string line, output, event;
170 llvm::raw_string_ostream x(output);
171 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i)
173 const Decl *decl = *i;
174 loc = decl->getLocation();
177 if(decl->getKind()==35)
181 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
183 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class
188 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
191 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
192 recursive_visit(declCont);
202 * It is used to recursive traverse decls in Namespaces. This method do the same as HandleTopLevelDecl.
204 void recursive_visit(const DeclContext *declCont)
206 string line, output, event;
207 llvm::raw_string_ostream x(output);
209 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
211 const Decl *decl = *i;
212 loc = decl->getLocation();
215 if(decl->getKind()==35)
219 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
221 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class
226 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
228 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
229 recursive_visit(declCont);
237 * This function works with class or struct. It splits the decl into 3 interesting parts.
238 * The state machine decl, state decl and event decl.
240 void struct_class(const Decl *decl)
242 string output, line, ret, trans, event;
243 llvm::raw_string_ostream x(output);
245 line = get_line_of_code(x.str());
249 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
252 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
254 if(find_events(cRecDecl, line))
256 events.push_back(namedDecl->getNameAsString());
258 else if(name_of_machine == "")
260 ret = find_name_of_machine(cRecDecl, line);
264 name_of_machine = ret.substr(0,pos);
265 name_of_start = ret.substr(pos+1);
270 ret = find_states(cRecDecl, line);
273 states.push_back(ret);
274 methods_in_class(decl,namedDecl->getNameAsString());
281 * This function provides traversing all methods and other context indide class. If
282 * typedef or classic method decl is found. If typedef is found then it is being testted for transitions and custom reactions.
284 void methods_in_class(const Decl *decl, const string state)
286 string output, line, ret, trans, event;
287 llvm::raw_string_ostream x(output);
289 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
290 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);
292 std::cout<<"Found state: "<<state<<std::endl;
293 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
295 if (i->getKind()==26) // typedefs
299 line = clean_spaces(cut_type(output));
300 ret = find_transitions(state,line);
303 num = count(ret,';')+1;
304 for(int i = 0;i<num;i++)
311 if(pos==-1) cReactions.push_back(ret);
312 else cReactions.push_back(ret.substr(0,pos));
317 if(pos==-1) transitions.push_back(ret);
318 else transitions.push_back(ret.substr(0,pos));
320 if(i!=num-1) ret = ret.substr(pos+1);
325 if(i->getKind()==35) method_decl(*i);// C++ method
331 * Traverse method declaration using classes with main class Stmt.
333 void method_decl(const Decl *decl)
335 string output, line, event;
336 llvm::raw_string_ostream x(output);
341 line = get_return(x.str());
342 if(get_model(line)==5)
344 //std::cout<<"metodass"<<std::endl;
345 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
346 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
347 QualType qt = pvd->getOriginalType();
348 event = qt.getAsString();
349 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
350 event = event.substr(event.rfind(" ")+1);
351 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
352 line = cut_namespaces(line.substr(0,line.rfind("::")));
355 find_return_stmt(decl->getBody(),line);
356 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++) // erase info about it from list of custom reactions
359 if(line.compare(event)==0)
369 void find_return_stmt(Stmt *statemt,string event) /** Traverse all statements in function for finding all return Statements.*/
371 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
374 for (Stmt::child_range range = statemt->children(); range; ++range)
376 test_stmt(*range, event);
381 void test_stmt(Stmt *stmt, string event) /** test statement for its kind Using number as identifier for all Statement Classes.*/
383 const SourceManager &sman = fsloc->getManager();
386 type = stmt->getStmtClass();
389 case 8 : find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
391 case 86 : find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
393 case 88 : find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
394 find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
396 case 90 : find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
398 case 98 : line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc());
399 line = get_line_of_code(line).substr(6);
400 line = line.substr(0,line.find("("));
401 if(get_model(line)==6)
403 param = get_params(line);
404 transitions.push_back(event.append(",").append(param));
406 if(get_model(line) == 7)
409 transitions.push_back(param.append(event));
412 case 99 : find_return_stmt(stmt, event);
414 case 101 : find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
416 case 102 : find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
423 * Main function provides all initialization before starting analysis of AST. Diagnostic Client is initialized,
424 * Command line options are processed.
426 int main(int argc, char **argv)
428 if(argc==1 || strncmp(argv[1],"-help",5)==0)
430 cout<<endl<<" Boost Statechart Viewer - help"<<endl;
431 cout<<"================================"<<endl;
432 cout<<"The program can be used almost the same way as a C compiler. You will typically need to specify locations for all header files except of the files stored in system folder(in Linux: /usr/...) using -I option. Of course you can specify the output filename (-o option). Program displays all diagnostic messages like compilers. If an error occurs the program stops."<<endl<<endl;
435 string inputFilename = "";
436 string outputFilename = "graph.dot"; // initialize output Filename
437 DiagnosticOptions dopts;
439 MyDiagnosticClient *mdc = new MyDiagnosticClient(llvm::errs(), dopts);
440 llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());
441 DiagnosticsEngine diag(dis,mdc);
442 FileManager fm( * new FileSystemOptions());
443 SourceManager sm (diag, fm);
445 Driver TheDriver(LLVM_BINDIR, llvm::sys::getDefaultTargetTriple(), "", false, diag);
446 TheDriver.setCheckInputsExist(true);
447 TheDriver.CCCIsCXX = 1;
448 TheDriver.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
450 CompilerInvocation compInv;
451 llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
452 llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args));
453 const driver::JobList &Jobs = C->getJobs();
454 const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
455 const driver::ArgStringList &CCArgs = Cmd->getArguments();
456 for(unsigned i = 0; i<Args.size();i++) // find -o in ArgStringList
458 if(strncmp(Args[i],"-o",2)==0)
460 if(strlen(Args[i])>2)
462 string str = Args[i];
463 outputFilename = str.substr(2);
465 else outputFilename = Args[i+1];
470 CompilerInvocation::CreateFromArgs(compInv,
471 const_cast<const char **>(CCArgs.data()),
472 const_cast<const char **>(CCArgs.data())+CCArgs.size(),
475 HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts();
476 LangOptions *lang = compInv.getLangOpts();
477 CompilerInvocation::setLangDefaults(*lang, IK_ObjCXX);
478 TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts());
479 FrontendOptions f = compInv.getFrontendOpts();
480 inputFilename = f.Inputs[0].File;
481 HeaderSearch *headers = new HeaderSearch(fm, diag, *lang, ti);
483 cout<<"Input filename: "<<inputFilename<<"\n"; // print Input filename
484 cout<<"Output filename: "<<outputFilename<<"\n\n\n"; // print Output filename
486 // Create a compiler instance to handle the actual work.
487 CompilerInstance Clang;
488 Clang.setInvocation(&compInv);
490 Preprocessor pp(diag, *lang, ti, sm, *headers, Clang);
491 pp.getBuiltinInfo().InitializeBuiltins(pp.getIdentifierTable(), *lang);
493 InitializePreprocessor(pp, compInv.getPreprocessorOpts(),hsopts,f);
495 const FileEntry *file = fm.getFile(inputFilename);
496 sm.createMainFileID(file);
497 IdentifierTable tab(*lang);
498 Builtin::Context builtins;
500 ASTContext ctx(*lang, sm, ti, tab, * new SelectorTable(), builtins,0);
501 mdc->BeginSourceFile(*lang, &pp);//start using diagnostic
502 ParseAST(pp, &c, ctx, false, TU_Complete);
503 mdc->EndSourceFile(); //end using diagnostic
504 IO_operations *io = new IO_operations(outputFilename, c.getStateMachine(), c.getNameOfFirstState(), c.getTransitions(), c.getStates(), c.getEvents());