diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp
index 15a7151a1e6d92df18c02f9c91e88e603661d683..763ed6ff1c693583d48e45355021e0e6c3e50733 100644
--- a/src/libs/cplusplus/pp-engine.cpp
+++ b/src/libs/cplusplus/pp-engine.cpp
@@ -1432,6 +1432,8 @@ bool Preprocessor::isQtReservedWord(const QByteArray &macroId) const
         return true;
     else if (size == 10 && macroId.at(0) == 'Q' && macroId == "Q_PROPERTY")
         return true;
+    else if (size == 18 && macroId.at(0) == 'Q' && macroId == "Q_PRIVATE_PROPERTY")
+        return true;
     else if (size == 7 && macroId.at(0) == 'Q' && macroId == "Q_ENUMS")
         return true;
     else if (size == 7 && macroId.at(0) == 'Q' && macroId == "Q_FLAGS")
diff --git a/src/plugins/cpptools/cppcodeformatter.cpp b/src/plugins/cpptools/cppcodeformatter.cpp
index fdb179f3a44e62b6ed7584c160298bc1bc99a659..d46c6c7271c3232ffd3c346b09fb67ebea26faa4 100644
--- a/src/plugins/cpptools/cppcodeformatter.cpp
+++ b/src/plugins/cpptools/cppcodeformatter.cpp
@@ -702,6 +702,7 @@ bool CodeFormatter::tryDeclaration()
     switch (kind) {
     case T_Q_ENUMS:
     case T_Q_PROPERTY:
+    case T_Q_PRIVATE_PROPERTY:
     case T_Q_FLAGS:
     case T_Q_GADGET:
     case T_Q_OBJECT:
diff --git a/src/shared/cplusplus/AST.cpp b/src/shared/cplusplus/AST.cpp
index 1db8baadeed379dcbd4b8f2a4c839c2d7870e5c0..0ef2b0e062098c6c87e6e4671eaa86704d12c156 100644
--- a/src/shared/cplusplus/AST.cpp
+++ b/src/shared/cplusplus/AST.cpp
@@ -3387,6 +3387,11 @@ unsigned QtPropertyDeclarationAST::firstToken() const
         return property_specifier_token;
     if (lparen_token)
         return lparen_token;
+    if (expression)
+        if (unsigned candidate = expression->firstToken())
+            return candidate;
+    if (comma_token)
+        return comma_token;
     if (type_id)
         if (unsigned candidate = type_id->firstToken())
             return candidate;
@@ -3415,6 +3420,11 @@ unsigned QtPropertyDeclarationAST::lastToken() const
     if (type_id)
         if (unsigned candidate = type_id->lastToken())
             return candidate;
+    if (comma_token)
+        return comma_token + 1;
+    if (expression)
+        if (unsigned candidate = expression->lastToken())
+            return candidate;
     if (lparen_token)
         return lparen_token + 1;
     if (property_specifier_token)
diff --git a/src/shared/cplusplus/AST.h b/src/shared/cplusplus/AST.h
index cacf686da687e93c4e088a2fdbf58db4a28d4df7..267ee29d970e6559bb4428ae5e50282071e10bd4 100644
--- a/src/shared/cplusplus/AST.h
+++ b/src/shared/cplusplus/AST.h
@@ -773,6 +773,8 @@ class CPLUSPLUS_EXPORT QtPropertyDeclarationAST: public DeclarationAST
 public:
     unsigned property_specifier_token;
     unsigned lparen_token;
+    ExpressionAST *expression; // for Q_PRIVATE_PROPERTY(expression, ...)
+    unsigned comma_token;
     ExpressionAST *type_id;
     NameAST *property_name;
     QtPropertyDeclarationItemListAST *property_declaration_item_list;
@@ -782,6 +784,8 @@ public:
     QtPropertyDeclarationAST()
         : property_specifier_token(0)
         , lparen_token(0)
+        , expression(0)
+        , comma_token(0)
         , type_id(0)
         , property_name(0)
         , property_declaration_item_list(0)
diff --git a/src/shared/cplusplus/ASTClone.cpp b/src/shared/cplusplus/ASTClone.cpp
index 3a7ac4782cad49aeb82db3d91f78b8937962b8f2..749f0ce7c30de2eb92615be30ff57cead70d5227 100644
--- a/src/shared/cplusplus/ASTClone.cpp
+++ b/src/shared/cplusplus/ASTClone.cpp
@@ -198,6 +198,9 @@ QtPropertyDeclarationAST *QtPropertyDeclarationAST::clone(MemoryPool *pool) cons
     QtPropertyDeclarationAST *ast = new (pool) QtPropertyDeclarationAST;
     ast->property_specifier_token = property_specifier_token;
     ast->lparen_token = lparen_token;
+    if (expression)
+        ast->expression = expression->clone(pool);
+    ast->comma_token = comma_token;
     if (type_id)
         ast->type_id = type_id->clone(pool);
     if (property_name)
diff --git a/src/shared/cplusplus/ASTMatcher.cpp b/src/shared/cplusplus/ASTMatcher.cpp
index e14a79f3fbce8fa6933dd791504210af95645d72..c853f6d4d1cf82f0b902558b7437e6901b509b2c 100644
--- a/src/shared/cplusplus/ASTMatcher.cpp
+++ b/src/shared/cplusplus/ASTMatcher.cpp
@@ -301,6 +301,13 @@ bool ASTMatcher::match(QtPropertyDeclarationAST *node, QtPropertyDeclarationAST
 
     pattern->lparen_token = node->lparen_token;
 
+    if (! pattern->expression)
+        pattern->expression = node->expression;
+    else if (! AST::match(node->expression, pattern->expression, this))
+        return false;
+
+    pattern->comma_token = node->comma_token;
+
     if (! pattern->type_id)
         pattern->type_id = node->type_id;
     else if (! AST::match(node->type_id, pattern->type_id, this))
diff --git a/src/shared/cplusplus/ASTVisit.cpp b/src/shared/cplusplus/ASTVisit.cpp
index 893339a2a83dc9c9ae4889ea77f68344501d2908..16ce0fc572e757c6a1ac2cbbf1aba90a6fc5b4ca 100644
--- a/src/shared/cplusplus/ASTVisit.cpp
+++ b/src/shared/cplusplus/ASTVisit.cpp
@@ -154,6 +154,7 @@ void QtPropertyDeclarationItemAST::accept0(ASTVisitor *visitor)
 void QtPropertyDeclarationAST::accept0(ASTVisitor *visitor)
 {
     if (visitor->visit(this)) {
+        accept(expression, visitor);
         accept(type_id, visitor);
         accept(property_name, visitor);
         accept(property_declaration_item_list, visitor);
diff --git a/src/shared/cplusplus/Keywords.cpp b/src/shared/cplusplus/Keywords.cpp
index 340f418547eaf26b0a3d42a2be23491f3304e3d8..ed32aeb021caa2f0fbda7361bae99300fcb4a3bb 100644
--- a/src/shared/cplusplus/Keywords.cpp
+++ b/src/shared/cplusplus/Keywords.cpp
@@ -1442,6 +1442,47 @@ static inline int classify14(const char *s, bool q, bool) {
   return T_IDENTIFIER;
 }
 
+static inline int classify18(const char *s, bool q, bool) {
+  if (q && s[0] == 'Q') {
+    if (s[1] == '_') {
+      if (s[2] == 'P') {
+        if (s[3] == 'R') {
+          if (s[4] == 'I') {
+            if (s[5] == 'V') {
+              if (s[6] == 'A') {
+                if (s[7] == 'T') {
+                  if (s[8] == 'E') {
+                    if (s[9] == '_') {
+                      if (s[10] == 'P') {
+                        if (s[11] == 'R') {
+                          if (s[12] == 'O') {
+                            if (s[13] == 'P') {
+                              if (s[14] == 'E') {
+                                if (s[15] == 'R') {
+                                  if (s[16] == 'T') {
+                                    if (s[17] == 'Y') {
+                                      return T_Q_PRIVATE_PROPERTY;
+                                    }
+                                  }
+                                }
+                              }
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  return T_IDENTIFIER;
+}
+
 static inline int classify19(const char *s, bool q, bool) {
   if (q && s[0] == 'Q') {
     if (s[1] == '_') {
@@ -1502,6 +1543,7 @@ int Lexer::classify(const char *s, int n, bool q, bool x) {
     case 13: return classify13(s, q, x);
     case 14: return classify14(s, q, x);
     case 16: return classify16(s, q, x);
+    case 18: return classify18(s, q, x);
     case 19: return classify19(s, q, x);
     default: return T_IDENTIFIER;
   } // switch
diff --git a/src/shared/cplusplus/Parser.cpp b/src/shared/cplusplus/Parser.cpp
index 4bd4a196aaf1660cbb8bc90957c6acee3e0fc316..d7b3dad29f1e549cc82ea094d0d5d2fe36a7536e 100644
--- a/src/shared/cplusplus/Parser.cpp
+++ b/src/shared/cplusplus/Parser.cpp
@@ -286,6 +286,7 @@ void Parser::skipUntilDeclaration()
         case T_Q_SIGNALS:
         case T_Q_SLOTS:
         case T_Q_PROPERTY:
+        case T_Q_PRIVATE_PROPERTY:
         case T_Q_ENUMS:
         case T_Q_FLAGS:
         case T_Q_INTERFACES:
@@ -1972,13 +1973,26 @@ bool Parser::parseAccessDeclaration(DeclarationAST *&node)
 bool Parser::parseQtPropertyDeclaration(DeclarationAST *&node)
 {
     DEBUG_THIS_RULE();
-    if (LA() != T_Q_PROPERTY)
+    const bool privateProperty = (LA() == T_Q_PRIVATE_PROPERTY);
+    if (LA() != T_Q_PROPERTY && !privateProperty)
         return false;
 
     QtPropertyDeclarationAST *ast = new (_pool)QtPropertyDeclarationAST;
     ast->property_specifier_token = consumeToken();
     if (LA() == T_LPAREN) {
         ast->lparen_token = consumeToken();
+
+        if (privateProperty) {
+            if (parsePostfixExpression(ast->expression)) {
+                match(T_COMMA, &ast->comma_token);
+            } else {
+                error(cursor(),
+                      "expected expression before `%s'",
+                      tok().spell());
+                return true;
+            }
+        }
+
         parseTypeId(ast->type_id);
 
         SimpleNameAST *property_name = new (_pool) SimpleNameAST;
@@ -2238,6 +2252,7 @@ bool Parser::parseMemberSpecification(DeclarationAST *&node, ClassSpecifierAST *
         return parseAccessDeclaration(node);
 
     case T_Q_PROPERTY:
+    case T_Q_PRIVATE_PROPERTY:
         return parseQtPropertyDeclaration(node);
 
     case T_Q_ENUMS:
diff --git a/src/shared/cplusplus/Token.h b/src/shared/cplusplus/Token.h
index 49175692d786399c1aa4ee09e0f4c053d278b4c6..bbc0149543b0d26ebc08f955a895f376938170aa 100644
--- a/src/shared/cplusplus/Token.h
+++ b/src/shared/cplusplus/Token.h
@@ -244,6 +244,7 @@ enum Kind {
     T_Q_Q,
     T_Q_INVOKABLE,
     T_Q_PROPERTY,
+    T_Q_PRIVATE_PROPERTY,
     T_Q_INTERFACES,
     T_Q_ENUMS,
     T_Q_FLAGS,
diff --git a/tests/tools/cplusplus-dump/dumpers.inc b/tests/tools/cplusplus-dump/dumpers.inc
index ec49d7aced927ef99e6c4469127adf7ffff06a4b..fdee4f1c1c145df1baafe0fca47d269154b2e1f7 100644
--- a/tests/tools/cplusplus-dump/dumpers.inc
+++ b/tests/tools/cplusplus-dump/dumpers.inc
@@ -198,6 +198,9 @@ virtual bool visit(QtPropertyDeclarationAST *ast)
         terminal(ast->property_specifier_token, ast);
     if (ast->lparen_token)
         terminal(ast->lparen_token, ast);
+    nonterminal(ast->expression);
+    if (ast->comma_token)
+        terminal(ast->comma_token, ast);
     nonterminal(ast->type_id);
     nonterminal(ast->property_name);
     for (QtPropertyDeclarationItemListAST *iter = ast->property_declaration_item_list; iter; iter = iter->next)