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"
44 #include "clang/Frontend/FrontendPluginRegistry.h"
45 #include "clang/AST/ASTConsumer.h"
46 #include "clang/AST/AST.h"
47 #include "clang/Frontend/CompilerInstance.h"
48 #include "llvm/Support/raw_ostream.h"
53 using namespace clang;
54 using namespace clang::driver;
58 * This class provides Simple diagnostic Client. It uses implementation in library for printing diagnostci information.
59 * Also it counts number of warnings, errors, ... When an error occurs the program is stopped.
61 class MyDiagnosticClient : public TextDiagnosticPrinter
63 int nwarnings; /** Save number of Warnings occured during diagnostic */
69 * Initialize number of warnings, errors, ...
71 MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false)
79 * This method prints diagnostic and counts diagnostic types.
81 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
83 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information using library implementation
84 switch (DiagLevel) // count number of all diagnostic information
86 case 0 : nignored+=1; break;
87 case 1 : nnotes+=1; break;
88 case 2 : nwarnings+=1; break;
95 * Print statistics about diagnostic.
99 cout<<"\n--Diagnostic Info--\n";
100 cout<<"Number of ignored: "<<nignored<<"\n";
101 cout<<"Number of notes: "<<nnotes<<"\n";
102 cout<<"Number of warnings: "<<nwarnings<<"\n";
103 cout<<"Number of errors and fatal errors: "<<nerrors<<"\n";
106 int getNbrOfWarnings() /** Return number of warnings */
111 int getNbrOfNotes() /** Return number of notes */
116 int getNbrOfIgnored() /** Return number of ignored */
123 * My ASTConsumer provides interface for traversing AST. It uses recursive traversing in namespaces.
125 class FindStates : public ASTConsumer
127 list<string> transitions;
128 list<string> cReactions; /** list of custom reactions. After all files are traversed this list should be empty. */
131 string name_of_machine;
132 string name_of_start;
134 FullSourceLoc *fsloc; /** Full Source Location instance for holding Source Manager. */
137 FindStates(string dest) : dest(dest) {}
139 list<string> getStates() /** Return list of states of the state machine. */
144 list<string> getTransitions() /** Return list of transitions. */
149 list<string> getEvents() /** Return list of events. */
154 string getStateMachine() /** Return name of the state machine. */
156 return name_of_machine;
159 string getNameOfFirstState() /** Return name of start state. */
161 return name_of_start;
164 virtual void Initialize(ASTContext &ctx)/** Run after the AST is constructed before the consumer starts to work. So this function works like constructor. */
166 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
168 name_of_machine = "";
172 * 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.
173 * When Namespace is found it recursively traverse all decls inside this Namespace using method recursive_visit.
175 virtual bool HandleTopLevelDecl(DeclGroupRef DGR)
178 string line, output, event;
179 llvm::raw_string_ostream x(output);
180 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i)
182 const Decl *decl = *i;
183 loc = decl->getLocation();
186 if(dyn_cast<FunctionDecl>(decl))
190 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
192 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class
197 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
200 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
201 recursive_visit(declCont);
211 * It is used to recursive traverse decls in Namespaces. This method do the same as HandleTopLevelDecl.
213 void recursive_visit(const DeclContext *declCont)
215 string line, output, event;
216 llvm::raw_string_ostream x(output);
218 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
220 const Decl *decl = *i;
221 loc = decl->getLocation();
224 if(dyn_cast<FunctionDecl>(decl))
228 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
230 if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class
235 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
237 DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
238 recursive_visit(declCont);
246 * This function works with class or struct. It splits the decl into 3 interesting parts.
247 * The state machine decl, state decl and event decl.
249 void struct_class(const Decl *decl)
251 string output, line, ret, trans, event;
252 llvm::raw_string_ostream x(output);
254 line = get_line_of_code(x.str());
258 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);
261 const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
263 if(find_events(cRecDecl, line))
265 events.push_back(namedDecl->getNameAsString());
267 else if(name_of_machine == "")
269 ret = find_name_of_machine(cRecDecl, line);
273 name_of_machine = ret.substr(0,pos);
274 name_of_start = ret.substr(pos+1);
279 ret = find_states(cRecDecl, line);
282 states.push_back(ret);
283 methods_in_class(decl,namedDecl->getNameAsString());
290 * This function provides traversing all methods and other context indide class. If
291 * typedef or classic method decl is found. If typedef is found then it is being testted for transitions and custom reactions.
293 void methods_in_class(const Decl *decl, const string state)
295 string output, line, ret, trans, event;
296 llvm::raw_string_ostream x(output);
298 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
299 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);
301 std::cout<<"Found state: "<<state<<std::endl;
302 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
304 if (i->getKind()==26) // typedefs
308 line = clean_spaces(cut_type(output));
309 ret = find_transitions(state,line);
312 num = count(ret,';')+1;
313 for(int i = 0;i<num;i++)
320 if(pos==-1) cReactions.push_back(ret);
321 else cReactions.push_back(ret.substr(0,pos));
326 if(pos==-1) transitions.push_back(ret);
327 else transitions.push_back(ret.substr(0,pos));
329 if(i!=num-1) ret = ret.substr(pos+1);
334 if (dyn_cast<FunctionDecl>(*i)) method_decl(*i);// C++ method
340 * Traverse method declaration using classes with main class Stmt.
342 void method_decl(const Decl *decl)
344 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
345 string output, line, event;
346 llvm::raw_string_ostream x(output);
348 if(fDecl->hasBody() && fDecl->getNumParams() > 0)
351 line = get_return(x.str());
352 if(get_model(line)==5)
354 //std::cout<<"metodass"<<std::endl;
355 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
356 QualType qt = pvd->getOriginalType();
357 event = qt.getAsString();
358 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
359 event = event.substr(event.rfind(" ")+1);
360 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
361 line = cut_namespaces(line.substr(0,line.rfind("::")));
364 find_return_stmt(decl->getBody(),line);
365 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++) // erase info about it from list of custom reactions
368 if(line.compare(event)==0)
378 void find_return_stmt(Stmt *statemt,string event) /** Traverse all statements in function for finding all return Statements.*/
380 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
383 for (Stmt::child_range range = statemt->children(); range; ++range)
385 test_stmt(*range, event);
390 void test_stmt(Stmt *stmt, string event) /** test statement for its kind Using number as identifier for all Statement Classes.*/
392 const SourceManager &sman = fsloc->getManager();
395 type = stmt->getStmtClass();
398 case 8 : find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
400 case 86 : find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
402 case 88 : find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
403 find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
405 case 90 : find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
407 case 98 : line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc());
408 line = get_line_of_code(line).substr(6);
409 line = line.substr(0,line.find("("));
410 if(get_model(line)==6)
412 param = get_params(line);
413 transitions.push_back(event.append(",").append(param));
415 if(get_model(line) == 7)
418 transitions.push_back(param.append(event));
421 case 99 : find_return_stmt(stmt, event);
423 case 101 : find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
425 case 102 : find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
430 virtual void HandleTranslationUnit(clang::ASTContext &Context) {
431 IO_operations io(dest, getStateMachine(), getNameOfFirstState(), getTransitions(), getStates(), getEvents());
437 class VisualizeStatechartAction : public PluginASTAction {
439 ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) {
440 size_t dot = getCurrentFile().find_last_of('.');
441 std::string dest = getCurrentFile().substr(0, dot);
443 return new FindStates(dest);
446 bool ParseArgs(const CompilerInstance &CI,
447 const std::vector<std::string>& args) {
448 for (unsigned i = 0, e = args.size(); i != e; ++i) {
449 llvm::errs() << "Visualizer arg = " << args[i] << "\n";
451 // Example error handling.
452 if (args[i] == "-an-error") {
453 DiagnosticsEngine &D = CI.getDiagnostics();
454 unsigned DiagID = D.getCustomDiagID(
455 DiagnosticsEngine::Error, "invalid argument '" + args[i] + "'");
460 if (args.size() && args[0] == "help")
461 PrintHelp(llvm::errs());
465 void PrintHelp(llvm::raw_ostream& ros) {
466 ros << "Help for Visualize Statechart plugin goes here\n";
471 static FrontendPluginRegistry::Add<VisualizeStatechartAction> X("visualize-statechart", "visualize statechart");