diff --git a/plugins/cpp/parser/src/clangastvisitor.h b/plugins/cpp/parser/src/clangastvisitor.h index d88f71c09..098514192 100644 --- a/plugins/cpp/parser/src/clangastvisitor.h +++ b/plugins/cpp/parser/src/clangastvisitor.h @@ -216,8 +216,11 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor if (_curFun->astNodeId && !_visitor->_functionStack.empty()) { - assert(_visitor->_scopeStack.Top() != nullptr && + StatementScope* scope = _visitor->_stmtStack.TopValid(); + assert(scope != nullptr && "Previous function entry has no corresponding scope stack entry."); + scope->EnsureConfigured(); + // If the currently parsed function had a total bumpiness of B // from S statements, and is nested inside an enclosing function // at depth D, then the total bumpiness of that enclosing function @@ -225,7 +228,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor // B (core nested bumpiness) + S * D (accounting for the indentation). model::CppFunctionPtr& prevFun = _visitor->_functionStack.top(); prevFun->bumpiness += _curFun->bumpiness + - _curFun->statementCount * _visitor->_scopeStack.Top()->Depth(); + _curFun->statementCount * scope->Depth(); prevFun->statementCount += _curFun->statementCount; } } @@ -276,7 +279,8 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor // at the beginning, and persists it at the end. FunctionScope fs(this); // We must also make an initial scope entry for the function's body. - NestedFunctionScope nfs(&_scopeStack, fd->getBody()); + StatementScope ss(&_stmtStack, nullptr); + ss.MakeFunction(fd->getBody()); return Base::TraverseDecl(d_); } else if (clang::RecordDecl* rd = llvm::dyn_cast(d_)) @@ -300,7 +304,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor ++_functionStack.top()->mccabe; } - void CountBumpiness(const NestedScope& scope_) + void CountBumpiness(const StatementScope& scope_) { if (!_functionStack.empty() && scope_.IsReal()) { @@ -378,6 +382,12 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor // Traverse Stmt + bool TraverseCompoundStmt(clang::CompoundStmt* cs_) + { + _stmtStack.Top()->MakeCompound(); + return Base::TraverseCompoundStmt(cs_); + } + bool TraverseDeclStmt(clang::DeclStmt* ds_) { CtxStmtScope css(this, ds_); @@ -392,42 +402,55 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool TraverseIfStmt(clang::IfStmt* is_) { + _stmtStack.Top()->MakeTwoWay(is_->getThen(), is_->getElse()); CountMcCabe(); return Base::TraverseIfStmt(is_); } bool TraverseWhileStmt(clang::WhileStmt* ws_) { + _stmtStack.Top()->MakeOneWay(ws_->getBody()); CountMcCabe(); return Base::TraverseWhileStmt(ws_); } bool TraverseDoStmt(clang::DoStmt* ds_) { + _stmtStack.Top()->MakeOneWay(ds_->getBody()); CountMcCabe(); return Base::TraverseDoStmt(ds_); } bool TraverseForStmt(clang::ForStmt* fs_) { + _stmtStack.Top()->MakeOneWay(fs_->getBody()); CountMcCabe(); return Base::TraverseForStmt(fs_); } bool TraverseCXXForRangeStmt(clang::CXXForRangeStmt* frs_) { + _stmtStack.Top()->MakeOneWay(frs_->getBody()); CountMcCabe(); return Base::TraverseCXXForRangeStmt(frs_); } + bool TraverseSwitchStmt(clang::SwitchStmt* ss_) + { + _stmtStack.Top()->MakeOneWay(ss_->getBody()); + return Base::TraverseSwitchStmt(ss_); + } + bool TraverseCaseStmt(clang::CaseStmt* cs_) { + _stmtStack.Top()->MakeTransparent(); CountMcCabe(); return Base::TraverseCaseStmt(cs_); } bool TraverseDefaultStmt(clang::DefaultStmt* ds_) { + _stmtStack.Top()->MakeTransparent(); CountMcCabe(); return Base::TraverseDefaultStmt(ds_); } @@ -444,8 +467,15 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor return Base::TraverseGotoStmt(gs_); } + bool TraverseCXXTryStmt(clang::CXXTryStmt* ts_) + { + _stmtStack.Top()->MakeOneWay(ts_->getTryBlock()); + return Base::TraverseCXXTryStmt(ts_); + } + bool TraverseCXXCatchStmt(clang::CXXCatchStmt* cs_) { + _stmtStack.Top()->MakeOneWay(cs_->getHandlerBlock()); CountMcCabe(); return Base::TraverseCXXCatchStmt(cs_); } @@ -458,42 +488,17 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor // Create a statement scope for the current statement for the duration // of its traversal. For every statement, there must be exactly one scope. - std::unique_ptr scope; - if (clang::IfStmt* is = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, is, is->getThen(), is->getElse()); - else if (clang::WhileStmt* ws = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, ws, ws->getBody()); - else if (clang::DoStmt* ds = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, ds, ds->getBody()); - else if (clang::ForStmt* fs = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, fs, fs->getBody()); - else if (clang::CXXForRangeStmt* frs = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, frs, frs->getBody()); - else if (clang::SwitchStmt* ss = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, ss, ss->getBody()); - else if (clang::CXXTryStmt* ts = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, ts, ts->getTryBlock()); - else if (clang::CXXCatchStmt* cs = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, cs, cs->getHandlerBlock()); - else if (clang::CompoundStmt* cs = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, cs); - else if (clang::SwitchCase* sc = llvm::dyn_cast(s_)) - scope = std::make_unique(&_scopeStack, sc); - else - // This is why we can't do this in the indidvidual handler functions: - // There is no Traverse* function equivalent to a general 'else' case - // when the current statement is neither of the above specified ones. - // By outsourcing scope creation to specialized handlers, - // we would lose the information of whether a specialized scope has - // already beeen created. - // We could theoretically have a general scope for every statement, - // plus extra scopes for specialized ones, but then each specialized - // statement would essentially be duplicated on the scope stack, - // which would disrupt the meaning of actual scopes in the code. - scope = std::make_unique(&_scopeStack, s_); - CountBumpiness(*scope); - - return Base::TraverseStmt(s_); + StatementScope scope(&_stmtStack, s_); + bool b = Base::TraverseStmt(s_); + // Base::TraverseStmt will select the best handler function for the + // statement's dynamic type. If it is not any of the special statement + // types we are explicitly handling, it must be a "normal" statement. + // In that case, none of the scope's specialized Make* member functions + // will be called. Even so, before counting its nestedness, + // we must ensure it is configured at least as a normal statement. + scope.EnsureConfigured(); + CountBumpiness(scope); + return b; } @@ -1109,22 +1114,23 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor bool VisitDeclStmt(clang::DeclStmt* ds_) { - clang::Stmt* scope; - if (_scopeStack.Top()->Statement() == ds_) + clang::Stmt* s; + StatementScope* scope = _stmtStack.TopValid(); + if (scope->Statement() == ds_) { - assert(_scopeStack.Top()->Previous() != nullptr && + assert(scope->Previous() != nullptr && "Declaration statement is not nested in a scope."); - scope = _scopeStack.Top()->Previous()->Statement(); + s = scope->Previous()->Statement(); } else - scope = _scopeStack.Top()->Statement(); + s = scope->Statement(); for (auto it = ds_->decl_begin(); it != ds_->decl_end(); ++it) { if (clang::VarDecl* vd = llvm::dyn_cast(*it)) { model::FileLoc loc = - getFileLoc(scope->getEndLoc(), scope->getEndLoc()); + getFileLoc(s->getEndLoc(), s->getEndLoc()); addDestructorUsage(vd->getType(), loc, vd); } } @@ -1830,7 +1836,7 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor std::unordered_map _locToAstType; std::unordered_map _locToAstValue; - NestedStack _scopeStack; + StatementStack _stmtStack; // This stack has the same role as _locTo* maps. In case of // clang::DeclRefExpr objects we need to determine the contect of the given // expression. In this stack we store the deepest statement node in AST which diff --git a/plugins/cpp/parser/src/nestedscope.cpp b/plugins/cpp/parser/src/nestedscope.cpp index fd7e90560..60b6435c4 100644 --- a/plugins/cpp/parser/src/nestedscope.cpp +++ b/plugins/cpp/parser/src/nestedscope.cpp @@ -5,15 +5,51 @@ namespace cc namespace parser { - NestedScope::NestedScope( - NestedStack* stack_, + StatementScope* StatementStack::TopValid() const + { + StatementScope* top = _top; + while (top != nullptr && top->_state == StatementScope::State::Invalid) + top = top->Previous(); + return top; + } + + + StatementScope::State StatementScope::CheckNext(clang::Stmt* stmt_) + { + if (_state == State::Invalid) + return State::Invalid; + EnsureConfigured(); + switch (_kind) + { + case Kind::Open: + return State::Standalone; + case Kind::Closed: + return State::Invalid; + case Kind::OneWay: + return (stmt_ == _exp0) + ? State::Expected : State::Invalid; + case Kind::TwoWay: + return (stmt_ == _exp0 || stmt_ == _exp1) + ? State::Expected : State::Invalid; + default: + assert(false && "This scope has not been configured yet."); + return State::Invalid; + } + } + + + StatementScope::StatementScope( + StatementStack* stack_, clang::Stmt* stmt_ ) : _stack(stack_), _previous(_stack->_top), _stmt(stmt_), _depth(0), - _state(State::Initial) + _state(State::Initial), + _kind(Kind::Unknown), + _exp0(nullptr), + _exp1(nullptr) { if (_previous != nullptr) { @@ -21,118 +57,110 @@ namespace parser if (_stmt != nullptr) _state = _previous->CheckNext(_stmt); } - - if (_state != State::Invalid) - _stack->_top = this; + _stack->_top = this; } - NestedScope::~NestedScope() + StatementScope::~StatementScope() { - if (_state != State::Invalid) - { - assert(_stack->_top == this && - "Scope destruction order has been violated."); - _stack->_top = _previous; - } + assert(_stack->_top == this && + "Scope destruction order has been violated."); + _stack->_top = _previous; } - NestedScope::State NestedTransparentScope::CheckNext(clang::Stmt*) const + void StatementScope::EnsureConfigured() + { + if (_kind == Kind::Unknown) + MakeStatement(); + } + + void StatementScope::MakeTransparent() { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // Anything inside a transparent statement block is standalone, // we do not expect any particular type of statement to be nested in it. - return State::Standalone; + _kind = Kind::Open; } - NestedTransparentScope::NestedTransparentScope( - NestedStack* stack_, - clang::Stmt* stmt_ - ) : - NestedScope(stack_, stmt_) - {} + void StatementScope::MakeCompound() + { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // Anything inside a compound statement block is standalone, + // we do not expect any particular type of statement to be nested in it. + _kind = Kind::Open; - NestedCompoundScope::NestedCompoundScope( - NestedStack* stack_, - clang::Stmt* stmt_ - ) : - NestedTransparentScope(stack_, stmt_) - { // A compound statement block only counts as a non-transparent scope // when it is not the expected body of any other statement. if (_state == State::Standalone) ++_depth; } - - NestedScope::State NestedStatementScope::CheckNext(clang::Stmt*) const + void StatementScope::MakeStatement() { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // A non-specialized statement scope is a single statement. // Anything inside a single statement (e.g sub-expressions) is not // something to be counted towards the total bumpiness of a function. - return State::Invalid; - } + _kind = Kind::Closed; - NestedStatementScope::NestedStatementScope( - NestedStack* stack_, - clang::Stmt* stmt_ - ) : - NestedScope(stack_, stmt_) - { // As long as the statement itself is valid on the stack, // it is a real scoped statement. if (_state != State::Invalid) ++_depth; } - - NestedScope::State NestedOneWayScope::CheckNext(clang::Stmt* stmt_) const + void StatementScope::MakeOneWay(clang::Stmt* next_) { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // A one-way scope expects a particular statement to be nested inside it. // Anything else above it on the stack is invalid. - return (stmt_ == _next) - ? State::Expected : State::Invalid; + _kind = Kind::OneWay; + _exp0 = next_; + + // As long as the statement itself is valid on the stack, + // it is a real scoped statement. + if (_state != State::Invalid) + ++_depth; } - NestedOneWayScope::NestedOneWayScope( - NestedStack* stack_, - clang::Stmt* stmt_, - clang::Stmt* next_ - ) : - NestedStatementScope(stack_, stmt_), - _next(next_) - {} + void StatementScope::MakeFunction(clang::Stmt* body_) + { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // A function scope expects its body to be nested inside it. + // Anything else above it on the stack is invalid. + _kind = Kind::OneWay; + _exp0 = body_; - NestedFunctionScope::NestedFunctionScope( - NestedStack* stack_, - clang::Stmt* next_ - ) : - NestedOneWayScope(stack_, nullptr, next_) - { // The level of nestedness always starts from zero at the function level. _depth = 0; } - - NestedScope::State NestedTwoWayScope::CheckNext(clang::Stmt* stmt_) const + void StatementScope::MakeTwoWay(clang::Stmt* next0_, clang::Stmt* next1_) { + assert(_kind == Kind::Unknown && + "Scope has already been configured; it cannot be reconfigured."); + // A two-way scope expects either of two particular statements to be // nested inside it. Anything else above it on the stack is invalid. - return (stmt_ == _next0 || stmt_ == _next1) - ? State::Expected : State::Invalid; - } + _kind = Kind::TwoWay; + _exp0 = next0_; + _exp1 = next1_; - NestedTwoWayScope::NestedTwoWayScope( - NestedStack* stack_, - clang::Stmt* stmt_, - clang::Stmt* next0_, - clang::Stmt* next1_ - ) : - NestedStatementScope(stack_, stmt_), - _next0(next0_), - _next1(next1_) - {} + // As long as the statement itself is valid on the stack, + // it is a real scoped statement. + if (_state != State::Invalid) + ++_depth; + } } } diff --git a/plugins/cpp/parser/src/nestedscope.h b/plugins/cpp/parser/src/nestedscope.h index 0c067b0c7..3b418c49e 100644 --- a/plugins/cpp/parser/src/nestedscope.h +++ b/plugins/cpp/parser/src/nestedscope.h @@ -8,43 +8,58 @@ namespace cc namespace parser { - class NestedScope; + class StatementScope; - class NestedStack final + class StatementStack final { - friend class NestedScope; + friend class StatementScope; private: - NestedScope* _top; + StatementScope* _top; public: - NestedScope* Top() const { return _top; } + StatementScope* Top() const { return _top; } + StatementScope* TopValid() const; - NestedStack() : _top(nullptr) {} + StatementStack() : _top(nullptr) {} }; - class NestedScope + class StatementScope final { + friend class StatementStack; + protected: enum class State : unsigned char { - Initial, - Expected, - Standalone, - Invalid, + Initial,// This statement is the root in its function. + Expected,// This statement was expected to be nested inside its parent. + Standalone,// This statement was not expected, but not forbidden either. + Invalid,// This statement was not expected to be on the stack. }; - NestedStack* _stack; - NestedScope* _previous; + enum class Kind : unsigned char + { + Unknown,// This scope has not been configured yet. + Open,// Any statement can be nested inside this statement. + Closed,// No other statement is allowed to be nested inside. + OneWay,// Only one specific statement is allowed to be nested inside. + TwoWay,// Only two specific statements are allowed to be nested inside. + }; + + StatementStack* _stack; + StatementScope* _previous; clang::Stmt* _stmt; unsigned int _depth; State _state; + Kind _kind; + clang::Stmt* _exp0; + clang::Stmt* _exp1; - virtual State CheckNext(clang::Stmt* stmt_) const = 0; + State CheckNext(clang::Stmt* stmt_); public: - NestedStack* Stack() const { return _stack; } - NestedScope* Previous() const { return _previous; } + StatementStack* Stack() const { return _stack; } + StatementScope* Previous() const { return _previous; } clang::Stmt* Statement() const { return _stmt; } unsigned int PrevDepth() const @@ -52,73 +67,16 @@ namespace parser unsigned int Depth() const { return _depth; } bool IsReal() const { return Depth() > PrevDepth(); } - NestedScope(NestedStack* stack_, clang::Stmt* stmt_); - virtual ~NestedScope(); - }; - - class NestedTransparentScope : public NestedScope - { - protected: - virtual State CheckNext(clang::Stmt* stmt_) const override; - - public: - NestedTransparentScope(NestedStack* stack_, clang::Stmt* stmt_); - }; - - class NestedCompoundScope : public NestedTransparentScope - { - public: - NestedCompoundScope(NestedStack* stack_, clang::Stmt* stmt_); - }; - - class NestedStatementScope : public NestedScope - { - protected: - virtual State CheckNext(clang::Stmt* stmt_) const override; - - public: - NestedStatementScope(NestedStack* stack_, clang::Stmt* stmt_); - }; + StatementScope(StatementStack* stack_, clang::Stmt* stmt_); + ~StatementScope(); - class NestedOneWayScope : public NestedStatementScope - { - protected: - clang::Stmt* _next; - - virtual State CheckNext(clang::Stmt* stmt_) const override; - - public: - NestedOneWayScope( - NestedStack* stack_, - clang::Stmt* stmt_, - clang::Stmt* next_ - ); - }; - - class NestedFunctionScope : public NestedOneWayScope - { - public: - NestedFunctionScope( - NestedStack* stack_, - clang::Stmt* next_ - ); - }; - - class NestedTwoWayScope : public NestedStatementScope - { - protected: - clang::Stmt* _next0; - clang::Stmt* _next1; - - virtual State CheckNext(clang::Stmt* stmt_) const override; - - public: - NestedTwoWayScope( - NestedStack* stack_, - clang::Stmt* stmt_, - clang::Stmt* next0_, - clang::Stmt* next1_ - ); + void EnsureConfigured(); + void MakeTransparent(); + void MakeCompound(); + void MakeStatement(); + void MakeOneWay(clang::Stmt* next_); + void MakeFunction(clang::Stmt* body_); + void MakeTwoWay(clang::Stmt* next0_, clang::Stmt* next1_); }; }