void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus11)
    return;

  // FIXME: Bunch of functionality that could be easily added:
  // + add handling of `push_front` for std::forward_list, std::list
  // and std::deque.
  // + add handling of `push` for std::stack, std::queue, std::priority_queue
  // + add handling of `insert` for stl associative container, but be careful
  // because this requires special treatment (it could cause performance
  // regression)
  // + match for emplace calls that should be replaced with insertion
  // + match for make_pair calls.
  auto callPushBack = cxxMemberCallExpr(
      hasDeclaration(functionDecl(hasName("push_back"))),
      on(hasType(cxxRecordDecl(hasAnyName("std::vector", "llvm::SmallVector",
                                          "std::list", "std::deque")))));

  // We can't replace push_backs of smart pointer because
  // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
  // passed pointer because smart pointer won't be constructed
  // (and destructed) as in push_back case.
  auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(
      ofClass(hasAnyName("std::shared_ptr", "std::unique_ptr", "std::auto_ptr",
                         "std::weak_ptr"))));

  // Bitfields binds only to consts and emplace_back take it by universal ref.
  auto bitFieldAsArgument = hasAnyArgument(ignoringParenImpCasts(
      memberExpr(hasDeclaration(fieldDecl(matchers::isBitfield())))));

  // We could have leak of resource.
  auto newExprAsArgument = hasAnyArgument(ignoringParenImpCasts(cxxNewExpr()));
  auto constructingDerived =
      hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));

  auto hasInitList = has(ignoringParenImpCasts(initListExpr()));
  auto soughtConstructExpr =
      cxxConstructExpr(
          unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument,
                       newExprAsArgument, constructingDerived,
                       has(materializeTemporaryExpr(hasInitList)))))
          .bind("ctor");
  auto hasConstructExpr = has(ignoringParenImpCasts(soughtConstructExpr));

  auto ctorAsArgument = materializeTemporaryExpr(
      anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));

  Finder->addMatcher(
      cxxMemberCallExpr(callPushBack, has(ctorAsArgument)).bind("call"), this);
}
void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) {
  // There are two ways to get into trouble with objects like FILE *:
  // dereferencing the pointer type to be a non-pointer type, and declaring
  // the type as a non-pointer type in the first place. While the declaration
  // itself could technically be well-formed in the case where the type is not
  // an opaque type, it's highly suspicious behavior.
  //
  // POSIX types are a bit different in that it's reasonable to declare a
  // non-pointer variable or data member of the type, but it is not reasonable
  // to dereference a pointer to the type, or declare a parameter of non-pointer
  // type.
  auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl"));
  auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).bind("type_decl"));
  auto BadEitherType = anyOf(BadFILEType, BadPOSIXType);

  Finder->addMatcher(
      namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType)))
          .bind("decl"),
      this);
  Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this);
  Finder->addMatcher(
      expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"),
      this);
}
void StringReferenceMemberCheck::registerMatchers(
    ast_matchers::MatchFinder *Finder) {
  // Look for const references to std::string or ::string.
  auto String = anyOf(recordDecl(hasName("::std::basic_string")),
                      recordDecl(hasName("::string")));
  auto ConstString = qualType(isConstQualified(), hasDeclaration(String));

  // Ignore members in template instantiations.
  auto InTemplateInstantiation = hasAncestor(
      decl(anyOf(recordDecl(ast_matchers::isTemplateInstantiation()),
                 functionDecl(ast_matchers::isTemplateInstantiation()))));

  Finder->addMatcher(fieldDecl(hasType(references(ConstString)),
                               unless(InTemplateInstantiation)).bind("member"),
                     this);
}
Пример #4
0
bool QoreTypeInfo::isOutputIdentical(const QoreTypeInfo* typeInfo) const {
   bool thisnt = (!hasType());
   bool typent = (!typeInfo->hasType());

   if (thisnt && typent)
      return true;

   if (thisnt || typent)
      return false;

   // from this point on, we know that both have types and are not NULL
   if ((returns_mult && !typeInfo->returns_mult)
       || (!returns_mult && typeInfo->returns_mult)) {
      //printd(5, "QoreTypeInfo::isOutputIdentical() lrm: %d rrm: %d\n", returns_mult, typeInfo->returns_mult);
      return false;
   }

   // from here on, we know either both accept single types or both accept multiple types
   if (!returns_mult)
      return isTypeIdenticalIntern(typeInfo);

   const type_vec_t &my_rt = getReturnTypeList();
   const type_vec_t &their_rt = typeInfo->getReturnTypeList();

   if (my_rt.size() != their_rt.size()) {
      //printd(5, "QoreTypeInfo::isOutputIdentical() lrts: %d rrts: %d\n", my_rt.size(), their_rt.size());
      return false;
   }

   // check all types to see if there is an identical type
   // FIXME: this is not very efficient (also only works properly if all types are unique in the list, which they should be)
   for (type_vec_t::const_iterator i = my_rt.begin(), e = my_rt.end(); i != e; ++i) {
      bool ident = false;
      for (type_vec_t::const_iterator j = their_rt.begin(), je = their_rt.end(); j != je; ++j) {
	 if ((*i)->isOutputIdentical(*j)) {
	    ident = true;
	    break;
	 }
      }
      if (!ident) {
         //printd(5, "QoreTypeInfo::isOutputIdentical() cannot find match for %s in rhs\n", (*i)->getName());
	 return false;
      }
   }

   return true;
}
Пример #5
0
bool QoreTypeInfo::isInputIdentical(const QoreTypeInfo* typeInfo) const {
   bool thisnt = (!hasType());
   bool typent = (!typeInfo->hasType());

   if (thisnt && typent)
      return true;

   if (thisnt || typent)
      return false;

   // from this point on, we know that both have types and are not NULL
   if ((accepts_mult && !typeInfo->accepts_mult)
       || (!accepts_mult && typeInfo->accepts_mult))
      return false;

   // from here on, we know either both accept single types or both accept multiple types
   if (!accepts_mult)
      return isTypeIdenticalIntern(typeInfo);

   const type_vec_t &my_at = getAcceptTypeList();
   const type_vec_t &their_at = typeInfo->getAcceptTypeList();

   if (my_at.size() != their_at.size())
      return false;

   // check all types to see if there is an identical type
   for (type_vec_t::const_iterator i = my_at.begin(), e = my_at.end(); i != e; ++i) {
      bool ident = false;
      for (type_vec_t::const_iterator j = their_at.begin(), je = their_at.end(); j != je; ++j) {
	 //printd(5, "QoreTypeInfo::isInputIdentical() this=%p i=%p %s j=%p %s\n", this, *i, (*i)->getName(), *j, (*j)->getName());

	 // if the second type is the original type, skip it
	 if (*j == this)
	    continue;

	 if ((*i) == (*j) || (*i)->isInputIdentical(*j)) {
	    ident = true;
	    break;
	 }
      }
      if (!ident)
	 return false;
   }

   return true;
}
void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) {
  const auto ConstParamDecl =
      parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
  Finder->addMatcher(
      functionDecl(unless(isDefinition()),
                   // Lambdas are always their own definition, but they
                   // generate a non-definition FunctionDecl too. Ignore those.
                   // Class template instantiations have a non-definition
                   // CXXMethodDecl for methods that aren't used in this
                   // translation unit. Ignore those, as the template will have
                   // already been checked.
                   unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf(
                       isLambda(), ast_matchers::isTemplateInstantiation()))))),
                   has(typeLoc(forEach(ConstParamDecl))))
          .bind("func"),
      this);
}
void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) {
  Finder->addMatcher(
      expr(unless(isInTemplateInstantiation()),
           expr(sizeOfExpr(has(ignoringParenImpCasts(
                    expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
                        matchesName("^(::std::|::string)"),
                        unless(matchesName("^::std::(bitset|array)$")),
                        hasMethod(cxxMethodDecl(hasName("size"), isPublic(),
                                                isConst())))))))))))
               .bind("sizeof"),
           // Ignore ARRAYSIZE(<array of containers>) pattern.
           unless(hasAncestor(binaryOperator(
               anyOf(hasOperatorName("/"), hasOperatorName("%")),
               hasLHS(ignoringParenCasts(sizeOfExpr(expr()))),
               hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))),
      this);
}
void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
  // Only register the matchers for C++11; the functionality currently does not
  // provide any benefit to other languages, despite being benign.
  if (!getLangOpts().CPlusPlus11)
    return;

  Finder->addMatcher(
      cxxConstructorDecl(
          unless(isImplicit()),
          allOf(isMoveConstructor(),
                hasAnyConstructorInitializer(
                    cxxCtorInitializer(
                        withInitializer(cxxConstructExpr(hasDeclaration(
                            cxxConstructorDecl(isCopyConstructor())
                                .bind("ctor")))))
                        .bind("move-init")))),
      this);

  auto NonConstValueMovableAndExpensiveToCopy =
      qualType(allOf(unless(pointerType()), unless(isConstQualified()),
                     hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl(
                         isMoveConstructor(), unless(isDeleted()))))),
                     matchers::isExpensiveToCopy()));

  // This checker is also used to implement cert-oop11-cpp, but when using that
  // form of the checker, we do not want to diagnose movable parameters.
  if (!UseCERTSemantics) {
    Finder->addMatcher(
        cxxConstructorDecl(
            allOf(
                unless(isMoveConstructor()),
                hasAnyConstructorInitializer(withInitializer(cxxConstructExpr(
                    hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
                    hasArgument(
                        0,
                        declRefExpr(
                            to(parmVarDecl(
                                   hasType(
                                       NonConstValueMovableAndExpensiveToCopy))
                                   .bind("movable-param")))
                            .bind("init-arg")))))))
            .bind("ctor-decl"),
        this);
  }
}
void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  auto MoveCallMatcher =
      callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
               unless(isInTemplateInstantiation()))
          .bind("call-move");

  Finder->addMatcher(MoveCallMatcher, this);

  auto ConstParamMatcher = forEachArgumentWithParam(
      MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified()))));

  Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this);
  Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"),
                     this);
}
void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  Finder->addMatcher(
      callExpr(
          hasDeclaration(functionDecl(
              returns(hasDeclaration(classTemplateSpecializationDecl(
                  hasName("std::basic_string"),
                  hasTemplateArgument(0,
                                      templateArgument().bind("char_type"))))),
              hasName("boost::lexical_cast"),
              hasParameter(0, hasType(qualType(has(substTemplateTypeParmType(
                                  isStrictlyInteger()))))))),
          argumentCountIs(1), unless(isInTemplateInstantiation()))
          .bind("to_string"),
      this);
}
void ProperlySeededRandomGeneratorCheck::registerMatchers(MatchFinder *Finder) {
  auto RandomGeneratorEngineDecl = cxxRecordDecl(hasAnyName(
      "::std::linear_congruential_engine", "::std::mersenne_twister_engine",
      "::std::subtract_with_carry_engine", "::std::discard_block_engine",
      "::std::independent_bits_engine", "::std::shuffle_order_engine"));
  auto RandomGeneratorEngineTypeMatcher = hasType(hasUnqualifiedDesugaredType(
      recordType(hasDeclaration(RandomGeneratorEngineDecl))));

  // std::mt19937 engine;
  // engine.seed();
  //        ^
  // engine.seed(1);
  //        ^
  // const int x = 1;
  // engine.seed(x);
  //        ^
  Finder->addMatcher(
      cxxMemberCallExpr(
          has(memberExpr(has(declRefExpr(RandomGeneratorEngineTypeMatcher)),
                         member(hasName("seed")),
                         unless(hasDescendant(cxxThisExpr())))))
          .bind("seed"),
      this);

  // std::mt19937 engine;
  //              ^
  // std::mt19937 engine(1);
  //              ^
  // const int x = 1;
  // std::mt19937 engine(x);
  //              ^
  Finder->addMatcher(
      cxxConstructExpr(RandomGeneratorEngineTypeMatcher).bind("ctor"), this);

  // srand();
  // ^
  // const int x = 1;
  // srand(x);
  // ^
  Finder->addMatcher(
      callExpr(callee(functionDecl(hasAnyName("::srand", "::std::srand"))))
          .bind("srand"),
      this);
}
void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
  if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions))
    return;

  // Match any static or thread_local variable declaration that has an
  // initializer that can throw.
  Finder->addMatcher(
      varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
              unless(anyOf(isConstexpr(), hasType(cxxRecordDecl(isLambda())),
                           hasAncestor(functionDecl()))),
              anyOf(hasDescendant(cxxConstructExpr(hasDeclaration(
                        cxxConstructorDecl(unless(isNoThrow())).bind("func")))),
                    hasDescendant(cxxNewExpr(hasDeclaration(
                        functionDecl(unless(isNoThrow())).bind("func")))),
                    hasDescendant(callExpr(hasDeclaration(
                        functionDecl(unless(isNoThrow())).bind("func"))))))
          .bind("var"),
      this);
}
void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
  auto Construct =
      cxxConstructExpr(
          hasDeclaration(cxxConstructorDecl(hasParent(
              cxxRecordDecl(unless(isTriviallyDefaultConstructible()))))))
          .bind("construct");

  Finder->addMatcher(
      cxxConstructorDecl(
          unless(isDelegatingConstructor()),
          ofClass(unless(
              anyOf(isUnion(), ast_matchers::isTemplateInstantiation()))),
          forEachConstructorInitializer(
              cxxCtorInitializer(isWritten(),
                                 withInitializer(ignoringImplicit(Construct)),
                                 unless(forField(hasType(isConstQualified()))))
                  .bind("init"))),
      this);
}
Пример #14
0
int QoreTypeInfo::acceptInputDefault(bool& priv_error, QoreValue& n) const {
   //printd(5, "QoreTypeInfo::acceptInputDefault() this=%p hasType=%d (%s) n=%p (%s)\n", this, hasType(), getName(), n, get_type_name(n));
   if (!hasType())
      return 0;

   if (!accepts_mult)
      return runtimeAcceptInputIntern(priv_error, n);

   const type_vec_t &at = getAcceptTypeList();

   // check all types until one accepts the input
   // priv_error can be set to false more than once; this is OK for error reporting
   for (type_vec_t::const_iterator i = at.begin(), e = at.end(); i != e; ++i) {
      assert((*i)->acceptsSingle());
      if (!(*i)->runtimeAcceptInputIntern(priv_error, n))
	 return 0;
   }

   return runtimeAcceptInputIntern(priv_error, n);
}
bool NumericRepresentationSetter::canUse(const QString &id) const
{
    try {
        bool ok;
        quint64 objid = id.toULongLong(&ok);
        if ( !ok)
            return false;
        Resource resource = mastercatalog()->id2Resource(objid);
        if ( !resource.isValid())
            return false;
        if ( resource.ilwisType() != itREPRESENTATION)
            return false;
        IRepresentation representation(resource);
        return hasType(representation->domain()->ilwisType(), itNUMERICDOMAIN);

    } catch(const ErrorObject& err){

    }
    return false;

}
void InefficientStringConcatenationCheck::registerMatchers(
    MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  const auto BasicStringType =
      hasType(qualType(hasUnqualifiedDesugaredType(recordType(
          hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))));

  const auto BasicStringPlusOperator = cxxOperatorCallExpr(
      hasOverloadedOperatorName("+"),
      hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))));

  const auto PlusOperator =
      cxxOperatorCallExpr(
          hasOverloadedOperatorName("+"),
          hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))),
          hasDescendant(BasicStringPlusOperator))
          .bind("plusOperator");

  const auto AssignOperator = cxxOperatorCallExpr(
      hasOverloadedOperatorName("="),
      hasArgument(0, declRefExpr(BasicStringType,
                                 hasDeclaration(decl().bind("lhsStrT")))
                         .bind("lhsStr")),
      hasArgument(1, stmt(hasDescendant(declRefExpr(
                         hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))),
      hasDescendant(BasicStringPlusOperator));

  if (StrictMode) {
    Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)),
                       this);
  } else {
    Finder->addMatcher(
        cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator),
                            hasAncestor(stmt(anyOf(cxxForRangeStmt(),
                                                   whileStmt(), forStmt())))),
        this);
  }
}
void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  const auto SingleChar =
      expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
  const auto StringFindFunctions =
      hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
                 "find_last_of", "find_last_not_of");

  Finder->addMatcher(
      cxxMemberCallExpr(
          callee(functionDecl(StringFindFunctions).bind("func")),
          anyOf(argumentCountIs(1), argumentCountIs(2)),
          hasArgument(0, SingleChar),
          on(expr(
              hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
                  recordDecl(hasAnyName(SmallVector<StringRef, 4>(
                      StringLikeClasses.begin(), StringLikeClasses.end()))))))),
              unless(hasSubstitutedType())))),
      this);
}
Пример #18
0
// if the argument's return type is compatible with "this"'s return type
bool QoreTypeInfo::isOutputCompatible(const QoreTypeInfo* typeInfo) const {
   if (!hasType())
      return true;

   if (!typeInfo->hasType())
      return false;

   // from this point on, we know that both have types and are not NULL
   if (!typeInfo->returns_mult) {
      //printd(5, "QoreTypeInfo::isOutputCompatible(%p '%s') this: %p '%s' (qc: %p qt: %d) ti->qc: %p ti->qt: %d\n", typeInfo, typeInfo->getName(), this, getName(), qc, qt, typeInfo->qc, typeInfo->qt);
      return typeInfo->qc ? parseReturnsClass(typeInfo->qc) : parseReturnsType(typeInfo->qt);
   }

   const type_vec_t &their_rt = typeInfo->getReturnTypeList();

   for (type_vec_t::const_iterator j = their_rt.begin(), je = their_rt.end(); j != je; ++j) {
      if (!isOutputCompatible(*j))
         return false;
   }

   return true;
}
void SignedBitwiseCheck::registerMatchers(MatchFinder *Finder) {
  const auto SignedIntegerOperand =
      expr(ignoringImpCasts(hasType(isSignedInteger()))).bind("signed-operand");

  // The standard [bitmask.types] allows some integral types to be implemented
  // as signed types. Exclude these types from diagnosing for bitwise or(|) and
  // bitwise and(&). Shifting and complementing such values is still not
  // allowed.
  const auto BitmaskType = namedDecl(anyOf(
      hasName("::std::locale::category"), hasName("::std::ctype_base::mask"),
      hasName("::std::ios_base::fmtflags"), hasName("::std::ios_base::iostate"),
      hasName("::std::ios_base::openmode")));
  const auto IsStdBitmask = ignoringImpCasts(declRefExpr(hasType(BitmaskType)));

  // Match binary bitwise operations on signed integer arguments.
  Finder->addMatcher(
      binaryOperator(
          allOf(anyOf(hasOperatorName("^"), hasOperatorName("|"),
                      hasOperatorName("&"), hasOperatorName("^="),
                      hasOperatorName("|="), hasOperatorName("&=")),

                unless(allOf(hasLHS(IsStdBitmask), hasRHS(IsStdBitmask))),

                hasEitherOperand(SignedIntegerOperand),
                hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))))
          .bind("binary-no-sign-interference"),
      this);

  // Shifting and complement is not allowed for any signed integer type because
  // the sign bit may corrupt the result.
  Finder->addMatcher(
      binaryOperator(
          allOf(anyOf(hasOperatorName("<<"), hasOperatorName(">>"),
                      hasOperatorName("<<="), hasOperatorName(">>=")),
                hasEitherOperand(SignedIntegerOperand),
                hasLHS(hasType(isInteger())), hasRHS(hasType(isInteger()))))
          .bind("binary-sign-interference"),
      this);

  // Match unary operations on signed integer types.
  Finder->addMatcher(unaryOperator(allOf(hasOperatorName("~"),
                                         hasUnaryOperand(SignedIntegerOperand)))
                         .bind("unary-signed"),
                     this);
}
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
  const auto Calc =
      expr(anyOf(binaryOperator(
                     anyOf(hasOperatorName("+"), hasOperatorName("-"),
                           hasOperatorName("*"), hasOperatorName("<<"))),
                 unaryOperator(hasOperatorName("~"))),
           hasType(isInteger()))
          .bind("Calc");

  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
                                             has(ignoringParenImpCasts(Calc)));
  const auto ImplicitCast =
      implicitCastExpr(hasImplicitDestinationType(isInteger()),
                       has(ignoringParenImpCasts(Calc)));
  const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");

  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
  Finder->addMatcher(
      binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
      this);
}
void UpgradeDurationConversionsCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus)
    return;

  // For the arithmetic calls, we match only the uses of the templated operators
  // where the template parameter is not a built-in type. This means the
  // instantiation makes use of an available user defined conversion to
  // `int64_t`.
  //
  // The implementation of these templates will be updated to fail SFINAE for
  // non-integral types. We match them to suggest an explicit cast.

  // Match expressions like `a *= b` and `a /= b` where `a` has type
  // `absl::Duration` and `b` is not of a built-in type.
  Finder->addMatcher(
      cxxOperatorCallExpr(
          argumentCountIs(2),
          hasArgument(
              0, expr(hasType(cxxRecordDecl(hasName("::absl::Duration"))))),
          hasArgument(1, expr().bind("arg")),
          callee(functionDecl(
              hasParent(functionTemplateDecl()),
              unless(hasTemplateArgument(0, refersToType(builtinType()))),
              hasAnyName("operator*=", "operator/=")))),
      this);

  // Match expressions like `a.operator*=(b)` and `a.operator/=(b)` where `a`
  // has type `absl::Duration` and `b` is not of a built-in type.
  Finder->addMatcher(
      cxxMemberCallExpr(
          callee(cxxMethodDecl(
              ofClass(cxxRecordDecl(hasName("::absl::Duration"))),
              hasParent(functionTemplateDecl()),
              unless(hasTemplateArgument(0, refersToType(builtinType()))),
              hasAnyName("operator*=", "operator/="))),
          argumentCountIs(1), hasArgument(0, expr().bind("arg"))),
      this);

  // Match expressions like `a * b`, `a / b`, `operator*(a, b)`, and
  // `operator/(a, b)` where `a` has type `absl::Duration` and `b` is not of a
  // built-in type.
  Finder->addMatcher(
      callExpr(callee(functionDecl(
                   hasParent(functionTemplateDecl()),
                   unless(hasTemplateArgument(0, refersToType(builtinType()))),
                   hasAnyName("::absl::operator*", "::absl::operator/"))),
               argumentCountIs(2),
               hasArgument(0, expr(hasType(
                                  cxxRecordDecl(hasName("::absl::Duration"))))),
               hasArgument(1, expr().bind("arg"))),
      this);

  // Match expressions like `a * b` and `operator*(a, b)` where `a` is not of a
  // built-in type and `b` has type `absl::Duration`.
  Finder->addMatcher(
      callExpr(callee(functionDecl(
                   hasParent(functionTemplateDecl()),
                   unless(hasTemplateArgument(0, refersToType(builtinType()))),
                   hasName("::absl::operator*"))),
               argumentCountIs(2), hasArgument(0, expr().bind("arg")),
               hasArgument(1, expr(hasType(cxxRecordDecl(
                                  hasName("::absl::Duration")))))),
      this);

  // For the factory functions, we match only the non-templated overloads that
  // take an `int64_t` parameter. Within these calls, we care about implicit
  // casts through a user defined conversion to `int64_t`.
  //
  // The factory functions will be updated to be templated and SFINAE on whether
  // the template parameter is an integral type. This complements the already
  // existing templated overloads that only accept floating point types.

  // Match calls like:
  //   `absl::Nanoseconds(x)`
  //   `absl::Microseconds(x)`
  //   `absl::Milliseconds(x)`
  //   `absl::Seconds(x)`
  //   `absl::Minutes(x)`
  //   `absl::Hours(x)`
  // where `x` is not of a built-in type.
  Finder->addMatcher(
      implicitCastExpr(
          anyOf(hasCastKind(CK_UserDefinedConversion),
                has(implicitCastExpr(hasCastKind(CK_UserDefinedConversion)))),
          hasParent(callExpr(
              callee(functionDecl(DurationFactoryFunction(),
                  unless(hasParent(functionTemplateDecl())))),
              hasArgument(0, expr().bind("arg"))))),
      this);
}
void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) {
  // Match relational operators.
  const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!"));
  const auto ComparisonBinaryOperator =
      binaryOperator(matchers::isComparisonOperator());
  const auto ComparisonOperator =
      expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator));

  // Add the list of known string compare-like functions and add user-defined
  // functions.
  std::vector<std::string> FunctionNames = utils::options::parseStringList(
      (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions)
          .str());

  // Match a call to a string compare functions.
  const auto FunctionCompareDecl =
      functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
                                                     FunctionNames.end())))
          .bind("decl");
  const auto DirectStringCompareCallExpr =
      callExpr(hasDeclaration(FunctionCompareDecl)).bind("call");
  const auto MacroStringCompareCallExpr = conditionalOperator(anyOf(
      hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)),
      hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr))));
  // The implicit cast is not present in C.
  const auto StringCompareCallExpr = ignoringParenImpCasts(
      anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr));

  if (WarnOnImplicitComparison) {
    // Detect suspicious calls to string compare:
    //     'if (strcmp())'  ->  'if (strcmp() != 0)'
    Finder->addMatcher(
        stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
                   whileStmt(hasCondition(StringCompareCallExpr)),
                   doStmt(hasCondition(StringCompareCallExpr)),
                   forStmt(hasCondition(StringCompareCallExpr)),
                   binaryOperator(
                       anyOf(hasOperatorName("&&"), hasOperatorName("||")),
                       hasEitherOperand(StringCompareCallExpr))))
            .bind("missing-comparison"),
        this);
  }

  if (WarnOnLogicalNotComparison) {
    // Detect suspicious calls to string compared with '!' operator:
    //     'if (!strcmp())'  ->  'if (strcmp() == 0)'
    Finder->addMatcher(unaryOperator(hasOperatorName("!"),
                                     hasUnaryOperand(ignoringParenImpCasts(
                                         StringCompareCallExpr)))
                           .bind("logical-not-comparison"),
                       this);
  }

  // Detect suspicious cast to an inconsistant type (i.e. not integer type).
  Finder->addMatcher(
      implicitCastExpr(unless(hasType(isInteger())),
                       hasSourceExpression(StringCompareCallExpr))
          .bind("invalid-conversion"),
      this);

  // Detect suspicious operator with string compare function as operand.
  Finder->addMatcher(
      binaryOperator(
          unless(anyOf(matchers::isComparisonOperator(), hasOperatorName("&&"),
                       hasOperatorName("||"), hasOperatorName("="))),
          hasEitherOperand(StringCompareCallExpr))
          .bind("suspicious-operator"),
      this);

  // Detect comparison to invalid constant: 'strcmp() == -1'.
  const auto InvalidLiteral = ignoringParenImpCasts(
      anyOf(integerLiteral(unless(equals(0))),
            unaryOperator(
                hasOperatorName("-"),
                has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))),
            characterLiteral(), cxxBoolLiteral()));

  Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(),
                                    hasEitherOperand(StringCompareCallExpr),
                                    hasEitherOperand(InvalidLiteral))
                         .bind("invalid-comparison"),
                     this);
}
Пример #23
0
void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
  if (!getLangOpts().CPlusPlus11)
    return;

  // FIXME: Bunch of functionality that could be easily added:
  // + add handling of `push_front` for std::forward_list, std::list
  // and std::deque.
  // + add handling of `push` for std::stack, std::queue, std::priority_queue
  // + add handling of `insert` for stl associative container, but be careful
  // because this requires special treatment (it could cause performance
  // regression)
  // + match for emplace calls that should be replaced with insertion
  // + match for make_pair calls.
  auto callPushBack = cxxMemberCallExpr(
      hasDeclaration(functionDecl(hasName("push_back"))),
      on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
          ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));

  // We can't replace push_backs of smart pointer because
  // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
  // passed pointer because smart pointer won't be constructed
  // (and destructed) as in push_back case.
  auto isCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
      SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));

  // Bitfields binds only to consts and emplace_back take it by universal ref.
  auto bitFieldAsArgument = hasAnyArgument(
      ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));

  // Initializer list can't be passed to universal reference.
  auto initializerListAsArgument = hasAnyArgument(
      ignoringImplicit(cxxConstructExpr(isListInitialization())));

  // We could have leak of resource.
  auto newExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
  // We would call another constructor.
  auto constructingDerived =
      hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));

  // emplace_back can't access private constructor.
  auto isPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));

  auto hasInitList = has(ignoringImplicit(initListExpr()));
  // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
  // overloaded functions and template names.
  auto soughtConstructExpr =
      cxxConstructExpr(
          unless(anyOf(isCtorOfSmartPtr, hasInitList, bitFieldAsArgument,
                       initializerListAsArgument, newExprAsArgument,
                       constructingDerived, isPrivateCtor)))
          .bind("ctor");
  auto hasConstructExpr = has(ignoringImplicit(soughtConstructExpr));

  auto ctorAsArgument = materializeTemporaryExpr(
      anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));

  Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument),
                                       unless(isInTemplateInstantiation()))
                         .bind("call"),
                     this);
}
#include "ASTUtility.h"

//StatementMatcher callExprMatcher = callExpr().bind("callexpr");
StatementMatcher mallocMatcher = callExpr(callee(functionDecl(hasName("malloc")).bind("m"))).bind("mallocCall");
StatementMatcher freeMatcher = callExpr(callee(functionDecl(hasName("free")).bind("f"))).bind("freeCall");
StatementMatcher reallocMatcher = callExpr(callee(functionDecl(hasName("realloc")).bind("r"))).bind("reallocCall");

//memcpy arrayType non-builtin = pointer or user-defined
StatementMatcher memcpyAryMatcher = callExpr(
        callee(functionDecl(hasName("memcpy")).bind("cpy")),
        hasAnyArgument(ignoringImpCasts(declRefExpr(
                    to(declaratorDecl(hasType(arrayType(
                                    unless(hasElementType(builtinType()))).bind("cpyParm")))))))
        ).bind("memcpyCall");
StatementMatcher memcpyPtrMatcher = callExpr(
        callee(functionDecl(hasName("memcpy")).bind("cpy")),
        hasAnyArgument(ignoringImpCasts(declRefExpr(
                    to(declaratorDecl(hasType(pointerType(
                                    pointee(unless(builtinType()))).bind("cpyParmPtr")))))))
        ).bind("memcpyCall");


StatementMatcher memcmpAryMatcher = callExpr(
        callee(functionDecl(hasName("memcmp")).bind("cmp")),
        hasAnyArgument(ignoringImpCasts(declRefExpr(
                    to(declaratorDecl(hasType(arrayType(
                                    unless(hasElementType(builtinType()))).bind("cmpParm")))))))
        ).bind("memcmpCall");
StatementMatcher memcmpPtrMatcher = callExpr(
        callee(functionDecl(hasName("memcmp")).bind("cmp")),
        hasAnyArgument(ignoringImpCasts(declRefExpr(
bool NumericRepresentationSetter::canUse(const IIlwisObject &obj, const ColumnDefinition &coldef) const
{
    if ( coldef.isValid())
        return hasType(coldef.datadef().domain()->ilwisType(), itNUMERICDOMAIN);
    return false;
}
Пример #26
0
void CloexecDupCheck::registerMatchers(MatchFinder *Finder) {
  registerMatchersImpl(Finder,
                       functionDecl(returns(isInteger()), hasName("dup"),
                                    hasParameter(0, hasType(isInteger()))));
}
Пример #27
0
#include "ASTUtility.h" 

// int *x = NULL or int *x = 0
DeclarationMatcher nullPointerMatcher = varDecl(hasType(pointerType()),
        hasInitializer(implicitCastExpr().bind("cast"))).bind("var");

// x == NULL  or x == 0
StatementMatcher biOpMatcher1 = binaryOperator(hasRHS(implicitCastExpr().bind("castR1")),
        hasOperatorName("==")).bind("bo1");

// x != NULL or x != 0
StatementMatcher biOpMatcher2 = binaryOperator(hasRHS(implicitCastExpr().bind("castR2")),
        hasOperatorName("!=")).bind("bo2");

// x != NULL or x != 0
StatementMatcher biOpMatcher3 = binaryOperator(hasRHS(implicitCastExpr().bind("castR3")),
        hasOperatorName("=")).bind("bo3");

class NullPointerPrinter : public MatchFinder::MatchCallback {
public:
    virtual void run(const MatchFinder::MatchResult &Result) 
    {
        //get the node
        clang::ASTContext *Context = Result.Context;
        const clang::ImplicitCastExpr *cast = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("cast");
        const clang::ImplicitCastExpr *castR1 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR1");
        const clang::ImplicitCastExpr *castR2 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR2");
        const clang::ImplicitCastExpr *castR3 = Result.Nodes.getNodeAs<clang::ImplicitCastExpr>("castR3");
        const clang::BinaryOperator *bo1 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo1");
        const clang::BinaryOperator *bo2 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo2");
        const clang::BinaryOperator *bo3 = Result.Nodes.getNodeAs<clang::BinaryOperator>("bo3");
Пример #28
0
void CanRunScriptChecker::registerMatchers(MatchFinder *AstMatcher) {
  auto InvalidArg =
      // We want to find any expression,
      ignoreTrivials(expr(
          // which has a refcounted pointer type,
          hasType(pointerType(
              pointee(hasDeclaration(cxxRecordDecl(isRefCounted()))))),
          // and which is not this,
          unless(cxxThisExpr()),
          // and which is not a method call on a smart ptr,
          unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))),
          // and which is not a parameter of the parent function,
          unless(declRefExpr(to(parmVarDecl()))),
          // and which is not a MOZ_KnownLive wrapped value.
          unless(callExpr(callee(functionDecl(hasName("MOZ_KnownLive"))))),
          expr().bind("invalidArg")));

  auto OptionalInvalidExplicitArg = anyOf(
      // We want to find any argument which is invalid.
      hasAnyArgument(InvalidArg),

      // This makes this matcher optional.
      anything());

  // Please not that the hasCanRunScriptAnnotation() matchers are not present
  // directly in the cxxMemberCallExpr, callExpr and constructExpr matchers
  // because we check that the corresponding functions can run script later in
  // the checker code.
  AstMatcher->addMatcher(
      expr(
          anyOf(
              // We want to match a method call expression,
              cxxMemberCallExpr(
                  // which optionally has an invalid arg,
                  OptionalInvalidExplicitArg,
                  // or which optionally has an invalid implicit this argument,
                  anyOf(
                      // which derefs into an invalid arg,
                      on(cxxOperatorCallExpr(
                          anyOf(hasAnyArgument(InvalidArg), anything()))),
                      // or is an invalid arg.
                      on(InvalidArg),

                      anything()),
                  expr().bind("callExpr")),
              // or a regular call expression,
              callExpr(
                  // which optionally has an invalid arg.
                  OptionalInvalidExplicitArg, expr().bind("callExpr")),
              // or a construct expression,
              cxxConstructExpr(
                  // which optionally has an invalid arg.
                  OptionalInvalidExplicitArg, expr().bind("constructExpr"))),

          anyOf(
              // We want to match the parent function.
              forFunction(functionDecl().bind("nonCanRunScriptParentFunction")),

              // ... optionally.
              anything())),
      this);
}
void DanglingOnTemporaryChecker::registerMatchers(MatchFinder *AstMatcher) {
  ////////////////////////////////////////
  // Quick annotation conflict checkers //
  ////////////////////////////////////////

  AstMatcher->addMatcher(
      // This is a matcher on a method declaration,
      cxxMethodDecl(
          // which is marked as no dangling on temporaries,
          noDanglingOnTemporaries(),

          // and which is && ref-qualified.
          isRValueRefQualified(),

          decl().bind("invalidMethodRefQualified")),
      this);

  AstMatcher->addMatcher(
      // This is a matcher on a method declaration,
      cxxMethodDecl(
          // which is marked as no dangling on temporaries,
          noDanglingOnTemporaries(),

          // which returns a primitive type,
          returns(builtinType()),

          // and which doesn't return a pointer.
          unless(returns(pointerType())),

          decl().bind("invalidMethodPointer")),
      this);

  //////////////////
  // Main checker //
  //////////////////

  auto hasParentCall =
      hasParent(expr(anyOf(
          cxxOperatorCallExpr(
              // If we're in a lamda, we may have an operator call expression
              // ancestor in the AST, but the temporary we're matching
              // against is not going to have the same lifetime as the
              // constructor call.
              unless(has(expr(ignoreTrivials(lambdaExpr())))),
              expr().bind("parentOperatorCallExpr")),
          callExpr(
              // If we're in a lamda, we may have a call expression
              // ancestor in the AST, but the temporary we're matching
              // against is not going to have the same lifetime as the
              // function call.
              unless(has(expr(ignoreTrivials(lambdaExpr())))),
              expr().bind("parentCallExpr")),
          objcMessageExpr(
              // If we're in a lamda, we may have an objc message expression
              // ancestor in the AST, but the temporary we're matching
              // against is not going to have the same lifetime as the
              // function call.
              unless(has(expr(ignoreTrivials(lambdaExpr())))),
              expr().bind("parentObjCMessageExpr")),
          cxxConstructExpr(
              // If we're in a lamda, we may have a construct expression
              // ancestor in the AST, but the temporary we're matching
              // against is not going to have the same lifetime as the
              // constructor call.
              unless(has(expr(ignoreTrivials(lambdaExpr())))),
              expr().bind("parentConstructExpr")))));

  AstMatcher->addMatcher(
      // This is a matcher on a method call,
      cxxMemberCallExpr(
          // which is in first party code,
          isFirstParty(),

          // and which is performed on a temporary,
          on(allOf(
              unless(hasType(pointerType())),
              isTemporary(),
              // but which is not `this`.
              unless(cxxThisExpr()))),

          // and which is marked as no dangling on temporaries.
          callee(cxxMethodDecl(noDanglingOnTemporaries())),

          expr().bind("memberCallExpr"),

          // We optionally match a parent call expression or a parent construct
          // expression because using a temporary inside a call is fine as long
          // as the pointer doesn't escape the function call.
          anyOf(
              // This is the case where the call is the direct parent, so we
              // know that the member call expression is the argument.
              allOf(hasParentCall, expr().bind("parentCallArg")),

              // This is the case where the call is not the direct parent, so we
              // get its child to know in which argument tree we are.
              hasAncestor(expr(
                  hasParentCall,
                  expr().bind("parentCallArg"))),
              // To make it optional.
              anything())),
      this);
}
Пример #30
0
    bool startElement(const QString &namespaceURI,
                      const QString &localName,
                      const QString &qName,
                      const QXmlAttributes &attributes)
    {
        // add to elements stack:
        m_elementStack.append(new ElementData(qName.utf8()));

        // First we need a node.
        if (DBUS("node"))
        {
            CONDITION(!m_currentNode.isEmpty(), "Node inside a node.");

            const int idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(idx < 0, QCString("Anonymous node found."));

            m_currentNode = attributes.value(idx).utf8();
            // A node is actually of little interest, so do nothing here.
            return true;
        }

        // Then we need an interface.
        if (DBUS("interface"))
        {
            // We need a nodeName for interfaces:
            CONDITION(m_currentNode.isEmpty(), "Interface without a node.");
            CONDITION(m_currentInterface, "Interface within another interface.");

            const int idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(idx < 0, QString("Interface without a name found."));

            // A interface is roughly equivalent to a class:
            m_currentInterface = createEntry();

            m_currentInterface->section = Entry::CLASS_SEC;
            m_currentInterface->spec |= Entry::Interface;
            m_currentInterface->type = "Interface";
            m_currentInterface->name = substitute(attributes.value(idx).utf8(), ".", "::");

            openScopes(m_currentInterface);

            return true;
        }

        if (DBUS("method") || DBUS("signal"))
        {
            // We need a interfaceName for methods and signals:
            CONDITION(!m_currentInterface, "Method or signal found outside a interface.");
            CONDITION(m_currentMethod, "Method or signal found inside another method or signal.");
            CONDITION(m_currentProperty, "Methor or signal found inside a property.");
            CONDITION(!m_structStack.isEmpty(), "Method or signal found inside a struct.");
            CONDITION(m_currentEnum, "Methor or signal found inside a enum.");

            const int idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(idx < 0, QString("Method or signal without a name found."));

            m_currentMethod = createEntry();

            m_currentMethod->section = Entry::FUNCTION_SEC;
            m_currentMethod->name = attributes.value(idx).utf8();
            m_currentMethod->mtype = Method;
            m_currentMethod->type = "void";

            if (DBUS("signal"))
            { m_currentMethod->mtype = Signal; }
        }

        if (DBUS("arg"))
        {
            // We need a method for arguments:
            CONDITION(!m_currentMethod, "Argument found outside a method or signal.");
            CONDITION(m_currentArgument, "Argument found inside another argument.");

            const int name_idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(name_idx < 0, QString("Argument without a name found."));
            COND_DOC_ERROR(!hasType(attributes), QString("Argument without a type found."));

            const int direction_idx(indexOf(attributes, "direction"));

            if ((m_currentMethod->mtype == Signal &&
                 direction_idx >= 0 &&
                 attributes.value(direction_idx) != "in") ||
                (m_currentMethod->mtype == Method &&
                 direction_idx >= 0 &&
                 attributes.value(direction_idx) != "in" &&
                 attributes.value(direction_idx) != "out"))
            {
                m_errorString = "Invalid direction found.";
                return false;
            }

            m_currentArgument = new Argument;
            m_currentArgument->type = getType(attributes).utf8();
            m_currentArgument->name = attributes.value(name_idx).utf8();
            if (direction_idx >= 0)
            { m_currentArgument->attrib = attributes.value(direction_idx).utf8(); }
            else
            {
                if (m_currentMethod->mtype == Signal)
                { m_currentArgument->attrib = "in"; }
                else
                { m_currentArgument->attrib = "out"; }
            }
        }

        if (DBUS("property"))
        {
            CONDITION(m_currentMethod, "Property found inside a method or signal.");
            CONDITION(!m_currentInterface, "Property found outside an interface.");
            CONDITION(m_currentProperty, "Property found inside another property.");
            CONDITION(!m_structStack.isEmpty(), "Property found inside a struct.");
            CONDITION(m_currentEnum, "Property found inside a enum.");

            const int name_idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(name_idx < 0, QString("Anonymous property found."));
            COND_DOC_ERROR(!hasType(attributes), QString("Property without a type found."));

            const int access_idx(indexOf(attributes, "access"));
            COND_DOC_ERROR(access_idx < 0, QString("Property without a access attribute found."));
            COND_DOC_ERROR(attributes.value(access_idx) != "read" &&
                           attributes.value(access_idx) != "write" &&
                           attributes.value(access_idx) != "readwrite",
                           QString("Property with invalid access attribute \"%1\" found.").
                           arg(attributes.value(access_idx)));

            m_currentProperty = createEntry();

            m_currentProperty->section = Entry::FUNCTION_SEC;

            if (attributes.value(access_idx) == "read" ||
                attributes.value(access_idx) == "readwrite")
            { m_currentProperty->spec |= Entry::Readable; }

            if (attributes.value(access_idx) == "write" ||
                attributes.value(access_idx) == "readwrite")
            { m_currentProperty->spec |= Entry::Writable; }

            m_currentProperty->name = attributes.value(name_idx).utf8();
            m_currentProperty->mtype = Property;
            m_currentProperty->type = getType(attributes).utf8();
        }

        if (EXTENSION("namespace"))
        {
            CONDITION(m_currentNode.isEmpty(), "Namespace found outside a node.");
            CONDITION(m_currentInterface, "Namespace found inside an interface.");

            const int idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(idx < 0, QString("Anonymous namespace found."));

            m_namespaceStack.append(openNamespace(attributes.value(idx)));
            openScopes(m_namespaceStack.last());
        }

        if (EXTENSION("struct"))
        {
            CONDITION(m_currentMethod, "Struct found inside a method or signal.");
            CONDITION(m_currentProperty, "Struct found inside a property.");
            CONDITION(m_currentEnum, "Struct found inside an enum.");

            const int idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(idx < 0, QString("Anonymous struct found."));

            Entry * current_struct = createEntry();
            current_struct->section = Entry::CLASS_SEC;
            current_struct->spec = Entry::Struct;
            current_struct->name = attributes.value(idx).utf8();

            openScopes(current_struct);

            current_struct->type = current_struct->name + " struct";

            m_structStack.append(new StructData(current_struct));
        }

        if (EXTENSION("member"))
        {
            CONDITION(m_structStack.isEmpty(), "Member found outside of struct.");

            const int name_idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(name_idx < 0, QString("Anonymous member found."));
            COND_DOC_ERROR(!hasType(attributes), QString("Member without a type found."));

            createEntry();

            m_currentEntry->section = Entry::VARIABLE_SEC;
            m_currentEntry->name = attributes.value(name_idx).utf8();
            m_currentEntry->type = getType(attributes).utf8();

            QString type(getDBusType(m_currentEntry->type));
            m_structStack.last()->type.append(type.utf8());
        }

        if (EXTENSION("enum") || EXTENSION("flagset"))
        {
            CONDITION(m_currentMethod, "Enum found inside a method or signal.");
            CONDITION(m_currentProperty, "Enum found inside a property.");

            const int name_idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(name_idx < 0, QString("Anonymous enum found."));

            const int type_idx(indexOf(attributes, "type"));
            QString type = "u";
            if (type_idx >= 0)
            { type = attributes.value(type_idx); }
            if (type != "y" && type != "q" && type != "u" && type != "t")
            { DOC_ERROR(QString("Invalid enum type \"%1\" found.").arg(type)); }

            m_currentEnum = createEntry();
            m_currentEnum->section = Entry::ENUM_SEC;
            m_currentEnum->name = attributes.value(name_idx).utf8();

            openScopes(m_currentEnum);

            m_currentEnum->type = m_currentEntry->name + " enum";

            addNamedType(type.utf8());
        }

        if (EXTENSION("value"))
        {
            CONDITION(!m_currentEnum, "Value found outside an enum.");

            const int name_idx(indexOf(attributes, "name"));
            COND_DOC_ERROR(name_idx < 0, QString("Anonymous value found."));

            const int value_idx(indexOf(attributes, "value"));

            createEntry();

            m_currentEntry->section = Entry::VARIABLE_SEC;
            m_currentEntry->name = attributes.value(name_idx).utf8();
            m_currentEntry->type = m_currentEnum->name; // "@"; // enum marker!
            if (value_idx >= 0)
            { m_currentEntry->initializer = attributes.value(value_idx).utf8(); }
        }

        return true;
    }