diff --git a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp --- a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp +++ b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp @@ -13,6 +13,7 @@ #include "clang-pseudo/grammar/LRTable.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/TokenKinds.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Debug.h" #include @@ -182,6 +183,25 @@ return true; } +bool isNotClassIdentifierFormTemplateParameter( + const GuardParams &TemplateParameter) { + const ForestNode *ParameterDeclaration = TemplateParameter.RHS[0]; + assert(ParameterDeclaration->symbol() == cxx::Symbol::parameter_declaration); + llvm::SmallVector Tokens; + // FIXME: This is an awkward way to get the end of the covered token range, + // pass it in the GuardParams? + Token::Index Start = ParameterDeclaration->startTokenIndex(); + if (TemplateParameter.Tokens.tokens()[Start].Kind != clang::tok::kw_class) + return true; + Token::Index Last = Start; + for (auto &Child : ParameterDeclaration->descendants()) { + if (Child.kind() == ForestNode::Terminal) + Last = std::max(Last, Child.startTokenIndex()); + } + return !(Last - Start == 1 && TemplateParameter.Tokens.tokens()[Last].Kind == + clang::tok::identifier); +} + // Whether this e.g. decl-specifier contains an "exclusive" type such as a class // name, and thus can't combine with a second exclusive type. // @@ -353,6 +373,17 @@ decl_specifier_seq__L_SQUARE__identifier_list__R_SQUARE__initializer__SEMI, specifiesStructuredBinding}, + // C++ [temp.param#2]: + // ...A template-parameter of the form class identifier is a type-parameter. + // + // template void f(T t); + // Here, the template f has a type-parameter called T, rather than an + // unnamed non-type template-parameter of class T. + { + rule::template_parameter::parameter_declaration, + isNotClassIdentifierFormTemplateParameter, + }, + // The grammar distinguishes (only) user-defined vs plain string literals, // where the clang lexer distinguishes (only) encoding types. {rule::user_defined_string_literal_chunk::STRING_LITERAL, diff --git a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf --- a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf +++ b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf @@ -663,7 +663,7 @@ constraint-logical-and-expression := primary-expression constraint-logical-and-expression := constraint-logical-and-expression && primary-expression template-parameter := type-parameter -template-parameter := parameter-declaration +template-parameter := parameter-declaration [guard] type-parameter := type-parameter-key ..._opt IDENTIFIER_opt type-parameter := type-parameter-key IDENTIFIER_opt = type-id type-parameter := type-constraint ..._opt IDENTIFIER_opt diff --git a/clang-tools-extra/pseudo/test/cxx/type-template-parameter.cpp b/clang-tools-extra/pseudo/test/cxx/type-template-parameter.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/pseudo/test/cxx/type-template-parameter.cpp @@ -0,0 +1,9 @@ +// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s + +// Verify that we don't misparse the "class abc" as an unnamed non-type template-parameter of class T. +template +struct Foo {}; + +// CHECK: template-parameter-list~type-parameter := type-parameter-key IDENTIFIER +// CHECK-NEXT: ├─type-parameter-key~CLASS := +// CHECK-NEXT: └─IDENTIFIER :=