X-Git-Url: http://rtime.felk.cvut.cz/gitweb/boost-statechart-viewer.git/blobdiff_plain/767436f553d1d183205753118148a53900650f40..25aa2a1c653bea6fbecdff650fe90201bb3e4810:/src/visualizer.cpp diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 503ebaf..a18b4c1 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -61,10 +61,12 @@ namespace Model class State : public Context { string initialInnerState; + list defferedEvents; public: const string name; explicit State(string name) : name(name) {} void setInitialInnerState(string name) { initialInnerState = name; } + void addDeferredEvent(const string &name) { defferedEvents.push_back(name); } friend ostream& operator<<(ostream& os, const State& s); }; @@ -88,12 +90,14 @@ namespace Model return 0; } - ostream& operator<<(ostream& os, const Context& c); ostream& operator<<(ostream& os, const State& s) { - os << indent << "" << s.name << "\n"; + string label = s.name; + for (list::const_iterator i = s.defferedEvents.begin(), e = s.defferedEvents.end(); i != e; ++i) + label.append("
").append(*i).append(" / defer"); + os << indent << s.name << " [label=<" << label << ">]\n"; if (s.size()) { os << indent << s.name << " -> " << s.initialInnerState << " [style = dashed]\n"; os << indent << "subgraph cluster_" << s.name << " {\n" << indent_inc; @@ -154,7 +158,7 @@ namespace Model class Model : public map { - Context unknown; // For forward-declared state classes + Context undefined; // For forward-declared state classes public: list< Transition*> transitions; @@ -166,14 +170,14 @@ namespace Model void addUndefinedState(State *m) { - unknown[m->name] = m; + undefined[m->name] = m; } Context *findContext(const string &name) { - Context::iterator ci = unknown.find(name); - if (ci != unknown.end()) + Context::iterator ci = undefined.find(name); + if (ci != undefined.end()) return ci->second; iterator i = find(name), e; if (i != end()) @@ -186,12 +190,23 @@ namespace Model return 0; } + State *findState(const string &name) + { + for (iterator i = begin(), e = end(); i != e; ++i) { + Context *c = i->second.findContext(name); + if (c) + return static_cast(c); + } + return 0; + } + + State *removeFromUndefinedContexts(const string &name) { - Context::iterator ci = unknown.find(name); - if (ci == unknown.end()) + Context::iterator ci = undefined.find(name); + if (ci == undefined.end()) return 0; - unknown.erase(ci); + undefined.erase(ci); return ci->second; } @@ -263,7 +278,7 @@ class Visitor : public RecursiveASTVisitor Model::Model &model; DiagnosticsEngine &Diags; unsigned diag_unhandled_reaction_type, diag_unhandled_reaction_decl, - diag_found_state, diag_found_statemachine, diag_no_history; + diag_found_state, diag_found_statemachine, diag_no_history, diag_warning; public: bool shouldVisitTemplateInstantiations() const { return true; } @@ -281,6 +296,8 @@ public: Diags.getCustomDiagID(DiagnosticsEngine::Error, "Unhandled reaction decl '%0'"); diag_unhandled_reaction_decl = Diags.getCustomDiagID(DiagnosticsEngine::Error, "History is not yet supported"); + diag_warning = + Diags.getCustomDiagID(DiagnosticsEngine::Warning, "'%0' %1"); } DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) { return Diags.Report(Loc, DiagID); } @@ -291,14 +308,20 @@ public: // TODO: Lookup for react even in base classes - probably by using Sema::LookupQualifiedName() for (DeclContext::lookup_const_result ReactRes = SrcState->lookup(DeclarationName(&II)); ReactRes.first != ReactRes.second; ++ReactRes.first) { - if (CXXMethodDecl *React = dyn_cast(*ReactRes.first)) - if (const ParmVarDecl *p = React->getParamDecl(0)) { + if (CXXMethodDecl *React = dyn_cast(*ReactRes.first)) { + if (React->getNumParams() >= 1) { + const ParmVarDecl *p = React->getParamDecl(0); const Type *ParmType = p->getType().getTypePtr(); if (ParmType->isLValueReferenceType()) ParmType = dyn_cast(ParmType)->getPointeeType().getTypePtr(); if (ParmType == EventType) FindTransitVisitor(model, SrcState, EventType).TraverseStmt(React->getBody()); - } + } else + Diag(React->getLocStart(), diag_warning) + << React << "has not a parameter"; + } else + Diag((*ReactRes.first)->getSourceRange().getBegin(), diag_warning) + << (*ReactRes.first)->getDeclKindName() << "is not supported as react method"; } } @@ -324,8 +347,9 @@ public: const Type *EventType = TST->getArg(0).getAsType().getTypePtr(); CXXRecordDecl *Event = EventType->getAsCXXRecordDecl(); - Model::Transition *T = new Model::Transition(SrcState->getName(), "\"??? deferral\"", Event->getName()); - model.transitions.push_back(T); + Model::State *s = model.findState(SrcState->getName()); + assert(s); + s->addDeferredEvent(Event->getName()); } else if (name == "boost::mpl::list") { for (TemplateSpecializationType::iterator Arg = TST->begin(), End = TST->end(); Arg != End; ++Arg) HandleReaction(Arg->getAsType().getTypePtr(), Loc, SrcState); @@ -344,39 +368,56 @@ public: Diag(Decl->getLocation(), diag_unhandled_reaction_decl) << Decl->getDeclKindName(); } - CXXRecordDecl *getTemplateArgDecl(const Type *T, unsigned ArgNum) + TemplateArgumentLoc getTemplateArgLoc(const TypeLoc &T, unsigned ArgNum) { - if (const ElaboratedType *ET = dyn_cast(T)) - return getTemplateArgDecl(ET->getNamedType().getTypePtr(), ArgNum); - else if (const TemplateSpecializationType *TST = dyn_cast(T)) { - if (TST->getNumArgs() >= ArgNum+1) - return TST->getArg(ArgNum).getAsType()->getAsCXXRecordDecl(); + if (const ElaboratedTypeLoc *ET = dyn_cast(&T)) + return getTemplateArgLoc(ET->getNamedTypeLoc(), ArgNum); + else if (const TemplateSpecializationTypeLoc *TST = dyn_cast(&T)) { + if (TST->getNumArgs() >= ArgNum+1) { + return TST->getArgLoc(ArgNum); + } else + Diag(TST->getBeginLoc(), diag_warning) << TST->getType()->getTypeClassName() << "has not enough arguments" << TST->getSourceRange(); + } else + Diag(T.getBeginLoc(), diag_warning) << T.getType()->getTypeClassName() << "type as template argument is not supported" << T.getSourceRange(); + return TemplateArgumentLoc(); + } + + TemplateArgumentLoc getTemplateArgLocOfBase(const CXXBaseSpecifier *Base, unsigned ArgNum) { + return getTemplateArgLoc(Base->getTypeSourceInfo()->getTypeLoc(), 1); + } + + CXXRecordDecl *getTemplateArgDeclOfBase(const CXXBaseSpecifier *Base, unsigned ArgNum, TemplateArgumentLoc &Loc) { + Loc = getTemplateArgLocOfBase(Base, 1); + switch (Loc.getArgument().getKind()) { + case TemplateArgument::Type: + return Loc.getTypeSourceInfo()->getType()->getAsCXXRecordDecl(); + case TemplateArgument::Null: + // Diag() was already called + break; + default: + Diag(Loc.getSourceRange().getBegin(), diag_warning) << Loc.getArgument().getKind() << "unsupported kind" << Loc.getSourceRange(); } return 0; } + CXXRecordDecl *getTemplateArgDeclOfBase(const CXXBaseSpecifier *Base, unsigned ArgNum) { + TemplateArgumentLoc Loc; + return getTemplateArgDeclOfBase(Base, ArgNum, Loc); + } - bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) + void handleSimpleState(CXXRecordDecl *RecordDecl, const CXXBaseSpecifier *Base) { - if (!Declaration->isCompleteDefinition()) - return true; - - MyCXXRecordDecl *RecordDecl = static_cast(Declaration); - const CXXBaseSpecifier *Base; - - if (RecordDecl->isDerivedFrom("boost::statechart::simple_state", &Base)) - { - string name(RecordDecl->getName()); //getQualifiedNameAsString()); - Diag(RecordDecl->getLocStart(), diag_found_state) << name; + string name(RecordDecl->getName()); //getQualifiedNameAsString()); + Diag(RecordDecl->getLocStart(), diag_found_state) << name; - Model::State *state; - // Either we saw a reference to forward declared state - // before, or we create a new state. - if (!(state = model.removeFromUndefinedContexts(name))) - // TODO: Fix the value of name - state = new Model::State(name); + Model::State *state; + // Either we saw a reference to forward declared state + // before, or we create a new state. + if (!(state = model.removeFromUndefinedContexts(name))) + state = new Model::State(name); - CXXRecordDecl *Context = getTemplateArgDecl(Base->getType().getTypePtr(), 1); + CXXRecordDecl *Context = getTemplateArgDeclOfBase(Base, 1); + if (Context) { Model::Context *c = model.findContext(Context->getName()); if (!c) { Model::State *s = new Model::State(Context->getName()); @@ -384,28 +425,54 @@ public: c = s; } c->add(state); + } - if (CXXRecordDecl *InnerInitialState = getTemplateArgDecl(Base->getType().getTypePtr(), 2)) + TemplateArgumentLoc Loc; + if (MyCXXRecordDecl *InnerInitialState = + static_cast(getTemplateArgDeclOfBase(Base, 2, Loc))) { + if (InnerInitialState->isDerivedFrom("boost::statechart::simple_state") || + InnerInitialState->isDerivedFrom("boost::statechart::state_machine")) state->setInitialInnerState(InnerInitialState->getName()); + else + Diag(Loc.getTypeSourceInfo()->getTypeLoc().getLocStart(), diag_warning) + << InnerInitialState->getName() << " as inner initial state is not supported" << Loc.getSourceRange(); + } // if (CXXRecordDecl *History = getTemplateArgDecl(Base->getType().getTypePtr(), 3)) // Diag(History->getLocStart(), diag_no_history); - IdentifierInfo& II = ASTCtx->Idents.get("reactions"); - // TODO: Lookup for reactions even in base classes - probably by using Sema::LookupQualifiedName() - for (DeclContext::lookup_result Reactions = RecordDecl->lookup(DeclarationName(&II)); - Reactions.first != Reactions.second; ++Reactions.first) - HandleReaction(*Reactions.first, RecordDecl); - } - else if (RecordDecl->isDerivedFrom("boost::statechart::state_machine", &Base)) - { - Model::Machine m(RecordDecl->getName()); - Diag(RecordDecl->getLocStart(), diag_found_statemachine) << m.name; + IdentifierInfo& II = ASTCtx->Idents.get("reactions"); + // TODO: Lookup for reactions even in base classes - probably by using Sema::LookupQualifiedName() + for (DeclContext::lookup_result Reactions = RecordDecl->lookup(DeclarationName(&II)); + Reactions.first != Reactions.second; ++Reactions.first) + HandleReaction(*Reactions.first, RecordDecl); + } - if (CXXRecordDecl *InitialState = getTemplateArgDecl(Base->getType().getTypePtr(), 1)) - m.setInitialState(InitialState->getName()); - model.add(m); - } + void handleStateMachine(CXXRecordDecl *RecordDecl, const CXXBaseSpecifier *Base) + { + Model::Machine m(RecordDecl->getName()); + Diag(RecordDecl->getLocStart(), diag_found_statemachine) << m.name; + + if (MyCXXRecordDecl *InitialState = + static_cast(getTemplateArgDeclOfBase(Base, 1))) + m.setInitialState(InitialState->getName()); + model.add(m); + } + + bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) + { + if (!Declaration->isCompleteDefinition()) + return true; + if (Declaration->getQualifiedNameAsString() == "boost::statechart::state") + return true; // This is an "abstract class" not a real state + + MyCXXRecordDecl *RecordDecl = static_cast(Declaration); + const CXXBaseSpecifier *Base; + + if (RecordDecl->isDerivedFrom("boost::statechart::simple_state", &Base)) + handleSimpleState(RecordDecl, Base); + else if (RecordDecl->isDerivedFrom("boost::statechart::state_machine", &Base)) + handleStateMachine(RecordDecl, Base); else if (RecordDecl->isDerivedFrom("boost::statechart::event")) { //sc.events.push_back(RecordDecl->getNameAsString());