| 1 | //===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "clang/AST/QualTypeNames.h" |
| 10 | #include "TestVisitor.h" |
| 11 | using namespace clang; |
| 12 | |
| 13 | namespace { |
| 14 | struct TypeNameVisitor : TestVisitor { |
| 15 | llvm::StringMap<std::string> ExpectedQualTypeNames; |
| 16 | bool WithGlobalNsPrefix = false; |
| 17 | |
| 18 | // ValueDecls are the least-derived decl with both a qualtype and a name. |
| 19 | bool VisitValueDecl(ValueDecl *VD) override { |
| 20 | std::string ExpectedName = |
| 21 | ExpectedQualTypeNames.lookup(Key: VD->getNameAsString()); |
| 22 | if (ExpectedName != "" ) { |
| 23 | PrintingPolicy Policy(Context->getPrintingPolicy()); |
| 24 | Policy.SuppressScope = false; |
| 25 | Policy.AnonymousTagLocations = true; |
| 26 | Policy.PolishForDeclaration = true; |
| 27 | Policy.SuppressUnwrittenScope = true; |
| 28 | std::string ActualName = TypeName::getFullyQualifiedName( |
| 29 | QT: VD->getType(), Ctx: *Context, Policy, WithGlobalNsPrefix); |
| 30 | if (ExpectedName != ActualName) { |
| 31 | // A custom message makes it much easier to see what declaration |
| 32 | // failed compared to EXPECT_EQ. |
| 33 | ADD_FAILURE() << "Typename::getFullyQualifiedName failed for " |
| 34 | << VD->getQualifiedNameAsString() << std::endl |
| 35 | << " Actual: " << ActualName << std::endl |
| 36 | << " Expected: " << ExpectedName; |
| 37 | } |
| 38 | } |
| 39 | return true; |
| 40 | } |
| 41 | }; |
| 42 | |
| 43 | // named namespaces inside anonymous namespaces |
| 44 | |
| 45 | TEST(QualTypeNameTest, Simple) { |
| 46 | TypeNameVisitor Visitor; |
| 47 | // Simple case to test the test framework itself. |
| 48 | Visitor.ExpectedQualTypeNames["CheckInt" ] = "int" ; |
| 49 | |
| 50 | // Keeping the names of the variables whose types we check unique |
| 51 | // within the entire test--regardless of their own scope--makes it |
| 52 | // easier to diagnose test failures. |
| 53 | |
| 54 | // Simple namespace qualifier |
| 55 | Visitor.ExpectedQualTypeNames["CheckA" ] = "A::B::Class0" ; |
| 56 | // Lookup up the enclosing scopes, then down another one. (These |
| 57 | // appear as elaborated type in the AST. In that case--even if |
| 58 | // policy.SuppressScope = 0--qual_type.getAsString(policy) only |
| 59 | // gives the name as it appears in the source, not the full name. |
| 60 | Visitor.ExpectedQualTypeNames["CheckB" ] = "A::B::C::Class1" ; |
| 61 | // Template parameter expansion. |
| 62 | Visitor.ExpectedQualTypeNames["CheckC" ] = |
| 63 | "A::B::Template0<A::B::C::MyInt, A::B::AnotherClass>" ; |
| 64 | // Recursive template parameter expansion. |
| 65 | Visitor.ExpectedQualTypeNames["CheckD" ] = |
| 66 | "A::B::Template0<A::B::Template1<A::B::C::MyInt, A::B::AnotherClass>, " |
| 67 | "A::B::Template0<int, long>>" ; |
| 68 | // Variadic Template expansion. |
| 69 | Visitor.ExpectedQualTypeNames["CheckE" ] = |
| 70 | "A::Variadic<int, A::B::Template0<int, char>, " |
| 71 | "A::B::Template1<int, long>, A::B::C::MyInt>" ; |
| 72 | // Using declarations should be fully expanded. |
| 73 | Visitor.ExpectedQualTypeNames["CheckF" ] = "A::B::Class0" ; |
| 74 | // Elements found within "using namespace foo;" should be fully |
| 75 | // expanded. |
| 76 | Visitor.ExpectedQualTypeNames["CheckG" ] = "A::B::C::MyInt" ; |
| 77 | // Type inside function |
| 78 | Visitor.ExpectedQualTypeNames["CheckH" ] = "struct X" ; |
| 79 | // Anonymous Namespaces |
| 80 | Visitor.ExpectedQualTypeNames["CheckI" ] = "aClass" ; |
| 81 | // Keyword inclusion with namespaces |
| 82 | Visitor.ExpectedQualTypeNames["CheckJ" ] = "struct A::aStruct" ; |
| 83 | // Anonymous Namespaces nested in named namespaces and vice-versa. |
| 84 | Visitor.ExpectedQualTypeNames["CheckK" ] = "D::aStruct" ; |
| 85 | // Namespace alias |
| 86 | Visitor.ExpectedQualTypeNames["CheckL" ] = "A::B::C::MyInt" ; |
| 87 | Visitor.ExpectedQualTypeNames["non_dependent_type_var" ] = |
| 88 | "Foo<X>::non_dependent_type" ; |
| 89 | Visitor.ExpectedQualTypeNames["AnEnumVar" ] = "EnumScopeClass::AnEnum" ; |
| 90 | Visitor.ExpectedQualTypeNames["AliasTypeVal" ] = "A::B::C::InnerAlias<int>" ; |
| 91 | Visitor.ExpectedQualTypeNames["AliasInnerTypeVal" ] = |
| 92 | "OuterTemplateClass<A::B::Class0>::Inner" ; |
| 93 | Visitor.ExpectedQualTypeNames["CheckM" ] = "const A::B::Class0 *" ; |
| 94 | Visitor.ExpectedQualTypeNames["CheckN" ] = "const X *" ; |
| 95 | Visitor.ExpectedQualTypeNames["ttp_using" ] = |
| 96 | "OuterTemplateClass<A::B::Class0>" ; |
| 97 | Visitor.ExpectedQualTypeNames["alias_of_template" ] = "ABTemplate0IntInt" ; |
| 98 | Visitor.runOver( |
| 99 | "int CheckInt;\n" |
| 100 | "template <typename T>\n" |
| 101 | "class OuterTemplateClass { public: struct Inner {}; };\n" |
| 102 | "namespace A {\n" |
| 103 | " namespace B {\n" |
| 104 | " class Class0 { };\n" |
| 105 | " namespace C {\n" |
| 106 | " typedef int MyInt;" |
| 107 | " template <typename T>\n" |
| 108 | " using InnerAlias = OuterTemplateClass<T>;\n" |
| 109 | " InnerAlias<int> AliasTypeVal;\n" |
| 110 | " InnerAlias<Class0>::Inner AliasInnerTypeVal;\n" |
| 111 | " }\n" |
| 112 | " template<class X, class Y> class Template0;" |
| 113 | " template<class X, class Y> class Template1;" |
| 114 | " typedef B::Class0 AnotherClass;\n" |
| 115 | " void Function1(Template0<C::MyInt,\n" |
| 116 | " AnotherClass> CheckC);\n" |
| 117 | " void Function2(Template0<Template1<C::MyInt, AnotherClass>,\n" |
| 118 | " Template0<int, long> > CheckD);\n" |
| 119 | " void Function3(const B::Class0* CheckM);\n" |
| 120 | " }\n" |
| 121 | "template<typename... Values> class Variadic {};\n" |
| 122 | "Variadic<int, B::Template0<int, char>, " |
| 123 | " B::Template1<int, long>, " |
| 124 | " B::C::MyInt > CheckE;\n" |
| 125 | " namespace BC = B::C;\n" |
| 126 | " BC::MyInt CheckL;\n" |
| 127 | "}\n" |
| 128 | "using A::B::Class0;\n" |
| 129 | "void Function(Class0 CheckF);\n" |
| 130 | "OuterTemplateClass<Class0> ttp_using;\n" |
| 131 | "using ABTemplate0IntInt = A::B::Template0<int, int>;\n" |
| 132 | "void Function(ABTemplate0IntInt alias_of_template);\n" |
| 133 | "using namespace A::B::C;\n" |
| 134 | "void Function(MyInt CheckG);\n" |
| 135 | "void f() {\n" |
| 136 | " struct X {} CheckH;\n" |
| 137 | "}\n" |
| 138 | "struct X;\n" |
| 139 | "void f(const ::X* CheckN) {}\n" |
| 140 | "namespace {\n" |
| 141 | " class aClass {};\n" |
| 142 | " aClass CheckI;\n" |
| 143 | "}\n" |
| 144 | "namespace A {\n" |
| 145 | " struct aStruct {} CheckJ;\n" |
| 146 | "}\n" |
| 147 | "namespace {\n" |
| 148 | " namespace D {\n" |
| 149 | " namespace {\n" |
| 150 | " class aStruct {};\n" |
| 151 | " aStruct CheckK;\n" |
| 152 | " }\n" |
| 153 | " }\n" |
| 154 | "}\n" |
| 155 | "template<class T> struct Foo {\n" |
| 156 | " typedef typename T::A dependent_type;\n" |
| 157 | " typedef int non_dependent_type;\n" |
| 158 | " dependent_type dependent_type_var;\n" |
| 159 | " non_dependent_type non_dependent_type_var;\n" |
| 160 | "};\n" |
| 161 | "struct X { typedef int A; };" |
| 162 | "Foo<X> var;" |
| 163 | "void F() {\n" |
| 164 | " var.dependent_type_var = 0;\n" |
| 165 | "var.non_dependent_type_var = 0;\n" |
| 166 | "}\n" |
| 167 | "class EnumScopeClass {\n" |
| 168 | "public:\n" |
| 169 | " enum AnEnum { ZERO, ONE };\n" |
| 170 | "};\n" |
| 171 | "EnumScopeClass::AnEnum AnEnumVar;\n" , |
| 172 | TypeNameVisitor::Lang_CXX11); |
| 173 | } |
| 174 | |
| 175 | TEST(QualTypeNameTest, Complex) { |
| 176 | TypeNameVisitor Complex; |
| 177 | Complex.ExpectedQualTypeNames["CheckTX" ] = "B::TX" ; |
| 178 | Complex.runOver( |
| 179 | "namespace A {" |
| 180 | " struct X {};" |
| 181 | "}" |
| 182 | "using A::X;" |
| 183 | "namespace fake_std {" |
| 184 | " template<class... Types > class tuple {};" |
| 185 | "}" |
| 186 | "namespace B {" |
| 187 | " using fake_std::tuple;" |
| 188 | " typedef tuple<X> TX;" |
| 189 | " TX CheckTX;" |
| 190 | " struct A { typedef int X; };" |
| 191 | "}" ); |
| 192 | } |
| 193 | |
| 194 | TEST(QualTypeNameTest, DoubleUsing) { |
| 195 | TypeNameVisitor DoubleUsing; |
| 196 | DoubleUsing.ExpectedQualTypeNames["direct" ] = "a::A<0>" ; |
| 197 | DoubleUsing.ExpectedQualTypeNames["indirect" ] = "b::B" ; |
| 198 | DoubleUsing.ExpectedQualTypeNames["double_indirect" ] = "b::B" ; |
| 199 | DoubleUsing.runOver(R"cpp( |
| 200 | namespace a { |
| 201 | template<int> class A {}; |
| 202 | A<0> direct; |
| 203 | } |
| 204 | namespace b { |
| 205 | using B = ::a::A<0>; |
| 206 | B indirect; |
| 207 | } |
| 208 | namespace b { |
| 209 | using ::b::B; |
| 210 | B double_indirect; |
| 211 | } |
| 212 | )cpp" ); |
| 213 | } |
| 214 | |
| 215 | TEST(QualTypeNameTest, GlobalNsPrefix) { |
| 216 | TypeNameVisitor GlobalNsPrefix; |
| 217 | GlobalNsPrefix.WithGlobalNsPrefix = true; |
| 218 | GlobalNsPrefix.ExpectedQualTypeNames["IntVal" ] = "int" ; |
| 219 | GlobalNsPrefix.ExpectedQualTypeNames["BoolVal" ] = "bool" ; |
| 220 | GlobalNsPrefix.ExpectedQualTypeNames["XVal" ] = "::A::B::X" ; |
| 221 | GlobalNsPrefix.ExpectedQualTypeNames["IntAliasVal" ] = "::A::B::Alias<int>" ; |
| 222 | GlobalNsPrefix.ExpectedQualTypeNames["ZVal" ] = "::A::B::Y::Z" ; |
| 223 | GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal" ] = "::Z" ; |
| 224 | GlobalNsPrefix.ExpectedQualTypeNames["CheckK" ] = "D::aStruct" ; |
| 225 | GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr" ] = "::A::B::X ::A::B::Y::Z::*" ; |
| 226 | GlobalNsPrefix.runOver( |
| 227 | "namespace A {\n" |
| 228 | " namespace B {\n" |
| 229 | " int IntVal;\n" |
| 230 | " bool BoolVal;\n" |
| 231 | " struct X {};\n" |
| 232 | " X XVal;\n" |
| 233 | " template <typename T> class CCC { };\n" |
| 234 | " template <typename T>\n" |
| 235 | " using Alias = CCC<T>;\n" |
| 236 | " Alias<int> IntAliasVal;\n" |
| 237 | " struct Y { struct Z { X YZIPtr; }; };\n" |
| 238 | " Y::Z ZVal;\n" |
| 239 | " X Y::Z::*YZMPtr;\n" |
| 240 | " }\n" |
| 241 | "}\n" |
| 242 | "struct Z {};\n" |
| 243 | "Z GlobalZVal;\n" |
| 244 | "namespace {\n" |
| 245 | " namespace D {\n" |
| 246 | " namespace {\n" |
| 247 | " class aStruct {};\n" |
| 248 | " aStruct CheckK;\n" |
| 249 | " }\n" |
| 250 | " }\n" |
| 251 | "}\n" |
| 252 | ); |
| 253 | } |
| 254 | |
| 255 | TEST(QualTypeNameTest, InlineNamespace) { |
| 256 | TypeNameVisitor InlineNamespace; |
| 257 | InlineNamespace.ExpectedQualTypeNames["c" ] = "B::C" ; |
| 258 | InlineNamespace.runOver("inline namespace A {\n" |
| 259 | " namespace B {\n" |
| 260 | " class C {};\n" |
| 261 | " }\n" |
| 262 | "}\n" |
| 263 | "using namespace A::B;\n" |
| 264 | "C c;\n" , |
| 265 | TypeNameVisitor::Lang_CXX11); |
| 266 | } |
| 267 | |
| 268 | TEST(QualTypeNameTest, TemplatedClass) { |
| 269 | std::unique_ptr<ASTUnit> AST = |
| 270 | tooling::buildASTFromCode(Code: "template <unsigned U1> struct A {\n" |
| 271 | " template <unsigned U2> struct B {};\n" |
| 272 | "};\n" |
| 273 | "template struct A<1>;\n" |
| 274 | "template struct A<2u>;\n" |
| 275 | "template struct A<1>::B<3>;\n" |
| 276 | "template struct A<2u>::B<4u>;\n" ); |
| 277 | |
| 278 | auto &Context = AST->getASTContext(); |
| 279 | auto &Policy = Context.getPrintingPolicy(); |
| 280 | auto getFullyQualifiedName = [&](QualType QT) { |
| 281 | return TypeName::getFullyQualifiedName(QT, Ctx: Context, Policy); |
| 282 | }; |
| 283 | |
| 284 | auto *A = Context.getTranslationUnitDecl() |
| 285 | ->lookup(&Context.Idents.get(Name: "A" )) |
| 286 | .find_first<ClassTemplateDecl>(); |
| 287 | ASSERT_NE(A, nullptr); |
| 288 | |
| 289 | // A has two explicit instantiations: A<1> and A<2u> |
| 290 | auto ASpec = A->spec_begin(); |
| 291 | ASSERT_NE(ASpec, A->spec_end()); |
| 292 | auto *A1 = *ASpec; |
| 293 | ASpec++; |
| 294 | ASSERT_NE(ASpec, A->spec_end()); |
| 295 | auto *A2 = *ASpec; |
| 296 | |
| 297 | // Their type names follow the records. |
| 298 | QualType A1RecordTy = Context.getRecordType(Decl: A1); |
| 299 | EXPECT_EQ(getFullyQualifiedName(A1RecordTy), "A<1>" ); |
| 300 | QualType A2RecordTy = Context.getRecordType(Decl: A2); |
| 301 | EXPECT_EQ(getFullyQualifiedName(A2RecordTy), "A<2U>" ); |
| 302 | |
| 303 | // getTemplateSpecializationType() gives types that print the integral |
| 304 | // argument directly. |
| 305 | TemplateArgument Args1[] = { |
| 306 | {Context, llvm::APSInt::getUnsigned(X: 1u), Context.UnsignedIntTy}}; |
| 307 | QualType A1TemplateSpecTy = Context.getTemplateSpecializationType( |
| 308 | TemplateName(A), Args1, Args1, A1RecordTy); |
| 309 | EXPECT_EQ(A1TemplateSpecTy.getAsString(), "A<1>" ); |
| 310 | |
| 311 | TemplateArgument Args2[] = { |
| 312 | {Context, llvm::APSInt::getUnsigned(X: 2u), Context.UnsignedIntTy}}; |
| 313 | QualType A2TemplateSpecTy = Context.getTemplateSpecializationType( |
| 314 | TemplateName(A), Args2, Args2, A2RecordTy); |
| 315 | EXPECT_EQ(A2TemplateSpecTy.getAsString(), "A<2>" ); |
| 316 | |
| 317 | // Find A<1>::B and its specialization B<3>. |
| 318 | auto *A1B = |
| 319 | A1->lookup(&Context.Idents.get(Name: "B" )).find_first<ClassTemplateDecl>(); |
| 320 | ASSERT_NE(A1B, nullptr); |
| 321 | auto A1BSpec = A1B->spec_begin(); |
| 322 | ASSERT_NE(A1BSpec, A1B->spec_end()); |
| 323 | auto *A1B3 = *A1BSpec; |
| 324 | QualType A1B3RecordTy = Context.getRecordType(Decl: A1B3); |
| 325 | EXPECT_EQ(getFullyQualifiedName(A1B3RecordTy), "A<1>::B<3>" ); |
| 326 | |
| 327 | // Construct A<1>::B<3> and check name. |
| 328 | TemplateArgument Args3[] = { |
| 329 | {Context, llvm::APSInt::getUnsigned(X: 3u), Context.UnsignedIntTy}}; |
| 330 | QualType A1B3TemplateSpecTy = Context.getTemplateSpecializationType( |
| 331 | TemplateName(A1B), Args3, Args3, A1B3RecordTy); |
| 332 | EXPECT_EQ(A1B3TemplateSpecTy.getAsString(), "B<3>" ); |
| 333 | |
| 334 | NestedNameSpecifier *A1Nested = NestedNameSpecifier::Create( |
| 335 | Context, Prefix: nullptr, T: A1TemplateSpecTy.getTypePtr()); |
| 336 | QualType A1B3ElaboratedTy = Context.getElaboratedType( |
| 337 | Keyword: ElaboratedTypeKeyword::None, NNS: A1Nested, NamedType: A1B3TemplateSpecTy); |
| 338 | EXPECT_EQ(A1B3ElaboratedTy.getAsString(), "A<1>::B<3>" ); |
| 339 | |
| 340 | // Find A<2u>::B and its specialization B<4u>. |
| 341 | auto *A2B = |
| 342 | A2->lookup(&Context.Idents.get(Name: "B" )).find_first<ClassTemplateDecl>(); |
| 343 | ASSERT_NE(A2B, nullptr); |
| 344 | auto A2BSpec = A2B->spec_begin(); |
| 345 | ASSERT_NE(A2BSpec, A2B->spec_end()); |
| 346 | auto *A2B4 = *A2BSpec; |
| 347 | QualType A2B4RecordTy = Context.getRecordType(Decl: A2B4); |
| 348 | EXPECT_EQ(getFullyQualifiedName(A2B4RecordTy), "A<2U>::B<4U>" ); |
| 349 | |
| 350 | // Construct A<2>::B<4> and check name. |
| 351 | TemplateArgument Args4[] = { |
| 352 | {Context, llvm::APSInt::getUnsigned(X: 4u), Context.UnsignedIntTy}}; |
| 353 | QualType A2B4TemplateSpecTy = Context.getTemplateSpecializationType( |
| 354 | TemplateName(A2B), Args4, Args4, A2B4RecordTy); |
| 355 | EXPECT_EQ(A2B4TemplateSpecTy.getAsString(), "B<4>" ); |
| 356 | |
| 357 | NestedNameSpecifier *A2Nested = NestedNameSpecifier::Create( |
| 358 | Context, Prefix: nullptr, T: A2TemplateSpecTy.getTypePtr()); |
| 359 | QualType A2B4ElaboratedTy = Context.getElaboratedType( |
| 360 | Keyword: ElaboratedTypeKeyword::None, NNS: A2Nested, NamedType: A2B4TemplateSpecTy); |
| 361 | EXPECT_EQ(A2B4ElaboratedTy.getAsString(), "A<2>::B<4>" ); |
| 362 | } |
| 363 | |
| 364 | TEST(QualTypeNameTest, AnonStrucs) { |
| 365 | TypeNameVisitor AnonStrucs; |
| 366 | AnonStrucs.ExpectedQualTypeNames["a" ] = "short" ; |
| 367 | AnonStrucs.ExpectedQualTypeNames["un_in_st_1" ] = |
| 368 | "union (unnamed struct at input.cc:1:1)::(unnamed union at " |
| 369 | "input.cc:2:27)" ; |
| 370 | AnonStrucs.ExpectedQualTypeNames["b" ] = "short" ; |
| 371 | AnonStrucs.ExpectedQualTypeNames["un_in_st_2" ] = |
| 372 | "union (unnamed struct at input.cc:1:1)::(unnamed union at " |
| 373 | "input.cc:5:27)" ; |
| 374 | AnonStrucs.ExpectedQualTypeNames["anon_st" ] = |
| 375 | "struct (unnamed struct at input.cc:1:1)" ; |
| 376 | AnonStrucs.runOver(R"(struct { |
| 377 | union { |
| 378 | short a; |
| 379 | } un_in_st_1; |
| 380 | union { |
| 381 | short b; |
| 382 | } un_in_st_2; |
| 383 | } anon_st;)" ); |
| 384 | } |
| 385 | |
| 386 | TEST(QualTypeNameTest, ConstUsing) { |
| 387 | TypeNameVisitor ConstUsing; |
| 388 | ConstUsing.ExpectedQualTypeNames["param1" ] = "const A::S &" ; |
| 389 | ConstUsing.ExpectedQualTypeNames["param2" ] = "const A::S" ; |
| 390 | ConstUsing.runOver(R"(namespace A { |
| 391 | class S {}; |
| 392 | } |
| 393 | using ::A::S; |
| 394 | void foo(const S& param1, const S param2);)" ); |
| 395 | } |
| 396 | |
| 397 | TEST(QualTypeNameTest, NullableAttributesWithGlobalNs) { |
| 398 | TypeNameVisitor Visitor; |
| 399 | Visitor.WithGlobalNsPrefix = true; |
| 400 | Visitor.ExpectedQualTypeNames["param1" ] = "::std::unique_ptr<int> _Nullable" ; |
| 401 | Visitor.ExpectedQualTypeNames["param2" ] = "::std::unique_ptr<int> _Nonnull" ; |
| 402 | Visitor.ExpectedQualTypeNames["param3" ] = |
| 403 | "::std::unique_ptr< ::std::unique_ptr<int> _Nullable> _Nonnull" ; |
| 404 | Visitor.ExpectedQualTypeNames["param4" ] = |
| 405 | "::std::unique_ptr<int> _Nullable const *" ; |
| 406 | Visitor.ExpectedQualTypeNames["param5" ] = |
| 407 | "::std::unique_ptr<int> _Nullable const *" ; |
| 408 | Visitor.ExpectedQualTypeNames["param6" ] = |
| 409 | "::std::unique_ptr<int> _Nullable const *" ; |
| 410 | Visitor.runOver(R"(namespace std { |
| 411 | template<class T> class unique_ptr {}; |
| 412 | } |
| 413 | void foo( |
| 414 | std::unique_ptr<int> _Nullable param1, |
| 415 | _Nonnull std::unique_ptr<int> param2, |
| 416 | std::unique_ptr<std::unique_ptr<int> _Nullable> _Nonnull param3, |
| 417 | const std::unique_ptr<int> _Nullable *param4, |
| 418 | _Nullable std::unique_ptr<int> const *param5, |
| 419 | std::unique_ptr<int> _Nullable const *param6 |
| 420 | ); |
| 421 | )" ); |
| 422 | } |
| 423 | } // end anonymous namespace |
| 424 | |