From b7ee7e01867e3e4f6f5e247ce02c783f4cb93833 Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 14 Sep 2023 15:22:02 -0400 Subject: [PATCH] Handle namespaces in parsing types, as seen in std::enable_if Big TODO in the test for better things to do with enable_if still. --- subdoc/lib/type.cc | 18 ++++++---- subdoc/tests/type_unittest.cc | 62 +++++++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/subdoc/lib/type.cc b/subdoc/lib/type.cc index 4226dde18..e44a769d9 100644 --- a/subdoc/lib/type.cc +++ b/subdoc/lib/type.cc @@ -241,19 +241,23 @@ Type build_local_type_internal( nested_names.push( TypeOrValue(TypeOrValueChoice::with( std::string(spec->getAsIdentifier()->getName())))); - } else { - if (kind != clang::NestedNameSpecifier::TypeSpec && - kind != clang::NestedNameSpecifier::TypeSpecWithTemplate) { - qualtype->dump(); - loc.dump(sm); - sus::unreachable(); - } + } else if (kind == clang::NestedNameSpecifier::Namespace || + kind == clang::NestedNameSpecifier::NamespaceAlias) { + // Namespaces are consumed/used as part of the type that comes after + // them. + } else if (kind == clang::NestedNameSpecifier::TypeSpec || + kind == clang::NestedNameSpecifier::TypeSpecWithTemplate) { nested_names.push( TypeOrValue(TypeOrValueChoice::with( build_local_type_internal( clang::QualType(spec->getAsType(), 0u), llvm::ArrayRef(), sm, preprocessor, loc)))); + } else { + qualtype->dump(); + loc.dump(sm); + fmt::println(stderr, "\nkind: {}", (int)kind); + sus::unreachable(); } spec = spec->getPrefix(); } diff --git a/subdoc/tests/type_unittest.cc b/subdoc/tests/type_unittest.cc index 682fae96f..1bbfd8bbd 100644 --- a/subdoc/tests/type_unittest.cc +++ b/subdoc/tests/type_unittest.cc @@ -1848,10 +1848,10 @@ TEST_F(SubDocTypeTest, VariadicConcept) { TEST_F(SubDocTypeTest, DependentNameType) { const char test[] = R"( - template struct R { using RType = T; }; - template struct S { using SType = T; }; + namespace a { template struct R { using RType = T; }; } + namespace b { template struct S { using SType = T; }; } template - void f(typename R>::RType::SType&); + void f(typename a::R>::RType::SType&); )"; run_test(test, [](clang::ASTContext& cx, clang::Preprocessor& preprocessor) { auto [qual, loc] = find_function_parm("f", cx).unwrap(); @@ -1865,6 +1865,12 @@ TEST_F(SubDocTypeTest, DependentNameType) { t.nested_names[0u].choice.as(); EXPECT_EQ(n1.category, subdoc::TypeCategory::Type); EXPECT_EQ(n1.name, "R"); + EXPECT_EQ(n1.namespace_path, sus::vec("a")); + const subdoc::Type& n1p1 = + n1.template_params[0u].choice.as(); + EXPECT_EQ(n1p1.category, subdoc::TypeCategory::Type); + EXPECT_EQ(n1p1.name, "S"); + EXPECT_EQ(n1p1.namespace_path, sus::vec("b")); const std::string& n2 = t.nested_names[1u].choice.as(); EXPECT_EQ(n2, "RType"); @@ -2211,4 +2217,54 @@ TEST_F(SubDocTypeTest, PointerToArrayOfPointers) { }); } +TEST_F(SubDocTypeTest, EnableIfReturn) { + const char test[] = R"( + namespace std { + template + struct enable_if {}; + + template + struct enable_if { using type = T; }; + } + + template + struct C { + static constexpr bool value = true; + }; + + struct S {}; + template + typename std::enable_if::value>::type f(); + )"; + run_test(test, [](clang::ASTContext& cx, clang::Preprocessor& preprocessor) { + auto [qual, loc] = find_function_return("f", cx).unwrap(); + subdoc::Type t = subdoc::build_local_type(qual, cx.getSourceManager(), + preprocessor, loc); + + // TODO: There's things that we could do better here, by parsing the + // expression in the enable_if, much like we'd like to parse expressions + // in requires clauses. + // - We could pull the whole condition of enable_if out as a + // constraint and show it in the requires list. So types could come with + // a constraint list. + // - Then the return type should just be the `type` argument of the + // enable_if statement, which defaults to void so is void here. + // + // Even more simply for now, without enable_if special cases, we could: + // - When we get to the template argument of type Expr, we could parse that + // and find all the types, instead of just turning it into a string! + EXPECT_EQ(t.category, subdoc::TypeCategory::TemplateVariable); + EXPECT_EQ(t.name, "type"); + EXPECT_EQ(t.refs, subdoc::Refs::None); + const subdoc::Type& t1 = + t.nested_names[0u].choice.as(); + EXPECT_EQ(t1.category, subdoc::TypeCategory::Type); + EXPECT_EQ(t1.name, "enable_if"); + EXPECT_EQ(t1.namespace_path, sus::vec("std")); + EXPECT_EQ(t1.nested_names.len(), 0u); + + EXPECT_EQ(make_string("foo", t), "!enable_if!::value>::type foo"); + }); +} + } // namespace