sammccall created this revision.
sammccall added a reviewer: kadircet.
Herald added subscribers: usaxena95, arphaman.
sammccall requested review of this revision.
Herald added subscribers: cfe-commits, sstefan1, MaskRay, ilya-biryukov.
Herald added a reviewer: jdoerfert.
Herald added a project: clang-tools-extra.

I started adding tests for all diags but found there were just too many cases.
Still I think cleaning up the tests and adding a few more is worthwhile.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D115484

Files:
  clang-tools-extra/clangd/IncludeFixer.cpp
  clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
  clang-tools-extra/clangd/unittests/TestIndex.cpp
  clang-tools-extra/clangd/unittests/TestIndex.h

Index: clang-tools-extra/clangd/unittests/TestIndex.h
===================================================================
--- clang-tools-extra/clangd/unittests/TestIndex.h
+++ clang-tools-extra/clangd/unittests/TestIndex.h
@@ -10,7 +10,6 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TESTINDEX_H
 
 #include "index/Index.h"
-#include "index/Merge.h"
 
 namespace clang {
 namespace clangd {
@@ -26,6 +25,8 @@
 Symbol func(llvm::StringRef Name);
 // Creates a class symbol.
 Symbol cls(llvm::StringRef Name);
+// Creates an enum symbol.
+Symbol enm(llvm::StringRef Name);
 // Creates a variable symbol.
 Symbol var(llvm::StringRef Name);
 // Creates a namespace symbol.
Index: clang-tools-extra/clangd/unittests/TestIndex.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TestIndex.cpp
+++ clang-tools-extra/clangd/unittests/TestIndex.cpp
@@ -65,6 +65,10 @@
   return sym(Name, index::SymbolKind::Class, "@S@\\0");
 }
 
+Symbol enm(llvm::StringRef Name) {
+  return sym(Name, index::SymbolKind::Enum, "@E@\\0");
+}
+
 Symbol var(llvm::StringRef Name) {
   return sym(Name, index::SymbolKind::Variable, "@\\0");
 }
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -70,6 +70,8 @@
   return Field(&Diag::Tags, Contains(TagMatcher));
 }
 
+MATCHER_P(HasRange, Range, "") { return arg.Range == Range; }
+
 MATCHER_P2(Diag, Range, Message,
            "Diag at " + llvm::to_string(Range) + " = [" + Message + "]") {
   return arg.Range == Range && arg.Message == Message;
@@ -831,95 +833,81 @@
 }
 
 TEST(IncludeFixerTest, IncompleteType) {
-  Annotations Test(R"cpp(// error-ok
-$insert[[]]namespace ns {
-  class X;
-  $nested[[X::]]Nested n;
-}
-class Y : $base[[public ns::X]] {};
-void test(ns::X *x, ns::X& ref_x) {
-  x$access[[->]]f();
-  auto& $type[[[]]a] = *x;
+  auto TU = TestTU::withHeaderCode("namespace ns { class X; } ns::X *x;");
+  TU.ExtraArgs.push_back("-std=c++20");
+  auto Index = buildIndexWithSymbol(
+      {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}});
+  TU.ExternalIndex = Index.get();
 
-  ns::X $incomplete[[var]];
-  $tag[[ref_x]]->f();
-  $use[[ns::X()]];
-  $sizeof[[sizeof]](ns::X);
-  for (auto it : $for[[ref_x]]);
+  std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
+      {"incomplete_nested_name_spec", "[[ns::X::]]Nested n;"},
+      {"incomplete_base_class", "class Y : [[ns::X]] {};"},
+      {"incomplete_member_access", "auto i = x[[->]]f();"},
+      {"incomplete_type", "auto& [[[]]m] = *x;"},
+      {"init_incomplete_type",
+       "struct C { static int f(ns::X&); }; int i = C::f([[{]]});"},
+      {"bad_cast_incomplete", "auto a = [[static_cast]]<ns::X>(0);"},
+      {"template_nontype_parm_incomplete", "template <ns::X [[foo]]> int a;"},
+      {"typecheck_decl_incomplete_type", "ns::X [[var]];"},
+      {"typecheck_incomplete_tag", "auto i = [[(*x)]]->f();"},
+      {"typecheck_nonviable_condition_incomplete",
+       "struct A { operator ns::X(); } a; const ns::X &[[b]] = a;"},
+      {"invalid_incomplete_type_use", "auto var = [[ns::X()]];"},
+      {"sizeof_alignof_incomplete_or_sizeless_type",
+       "auto s = [[sizeof]](ns::X);"},
+      {"for_range_incomplete_type", "void foo() { for (auto i : [[*]]x ) {} }"},
+      {"func_def_incomplete_result", "ns::X [[func]] () {}"},
+      {"field_incomplete_or_sizeless", "class M { ns::X [[member]]; };"},
+      {"array_incomplete_or_sizeless_type", "auto s = [[(ns::X[]){}]];"},
+      {"call_incomplete_return", "ns::X f(); auto fp = &f; auto z = [[fp()]];"},
+      {"call_function_incomplete_return", "ns::X foo(); auto a = [[foo()]];"},
+      {"call_incomplete_argument", "int m(ns::X); int i = m([[*x]]);"},
+      {"switch_incomplete_class_type", "void a() { [[switch]](*x) {} }"},
+      {"delete_incomplete_class_type", "void f() { [[delete]] *x; }"},
+      {"-Wdelete-incomplete", "void f() { [[delete]] x; }"},
+      {"dereference_incomplete_type",
+       R"cpp(void f() { asm("" : "=r"([[*]]x)::); })cpp"},
+  };
+  for (auto Case : Tests) {
+    Annotations Main(Case.second);
+    TU.Code = Main.code().str() + "\n // error-ok";
+    EXPECT_THAT(
+        *TU.build().getDiagnostics(),
+        ElementsAre(AllOf(DiagName(Case.first), HasRange(Main.range()),
+                          WithFix(Fix(Range{}, "#include \"x.h\"\n",
+                                      "Include \"x.h\" for symbol ns::X")))))
+        << Case.second;
+  }
 }
 
-ns::X $return[[func]]() {}
+TEST(IncludeFixerTest, IncompleteEnum) {
+  Symbol Sym = enm("X");
+  Sym.Flags |= Symbol::IndexedForCodeCompletion;
+  Sym.CanonicalDeclaration.FileURI = Sym.Definition.FileURI = "unittest:///x.h";
+  Sym.IncludeHeaders.emplace_back("\"x.h\"", 1);
+  SymbolSlab::Builder Slab;
+  Slab.insert(Sym);
+  auto Index =
+      MemIndex::build(std::move(Slab).build(), RefSlab(), RelationSlab());
 
-class T {
-  ns::X $field[[x]];
-};
-  )cpp");
-  auto TU = TestTU::withCode(Test.code());
-  TU.ExtraArgs.push_back("-std=c++17");
-  auto Index = buildIndexWithSymbol(
-      {SymbolWithHeader{"ns::X", "unittest:///x.h", "\"x.h\""}});
+  TestTU TU;
   TU.ExternalIndex = Index.get();
+  TU.ExtraArgs.push_back("-std=c++20");
 
-  EXPECT_THAT(
-      *TU.build().getDiagnostics(),
-      UnorderedElementsAreArray(
-          {AllOf(Diag(Test.range("nested"),
-                      "incomplete type 'ns::X' named in nested name specifier"),
-                 DiagName("incomplete_nested_name_spec"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("base"), "base class has incomplete type"),
-                 DiagName("incomplete_base_class"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("access"),
-                      "member access into incomplete type 'ns::X'"),
-                 DiagName("incomplete_member_access"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(
-               Diag(
-                   Test.range("type"),
-                   "incomplete type 'ns::X' where a complete type is required"),
-               DiagName("incomplete_type"),
-               WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                           "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("incomplete"),
-                      "variable has incomplete type 'ns::X'"),
-                 DiagName("typecheck_decl_incomplete_type"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(
-               Diag(Test.range("tag"), "incomplete definition of type 'ns::X'"),
-               DiagName("typecheck_incomplete_tag"),
-               WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                           "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("use"),
-                      "invalid use of incomplete type 'ns::X'"),
-                 DiagName("invalid_incomplete_type_use"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("sizeof"),
-                      "invalid application of 'sizeof' to "
-                      "an incomplete type 'ns::X'"),
-                 DiagName("sizeof_alignof_incomplete_or_sizeless_type"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("for"),
-                      "cannot use incomplete type 'ns::X' as a range"),
-                 DiagName("for_range_incomplete_type"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("return"),
-                      "incomplete result type 'ns::X' in function definition"),
-                 DiagName("func_def_incomplete_result"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X"))),
-           AllOf(Diag(Test.range("field"), "field has incomplete type 'ns::X'"),
-                 DiagName("field_incomplete_or_sizeless"),
-                 WithFix(Fix(Test.range("insert"), "#include \"x.h\"\n",
-                             "Include \"x.h\" for symbol ns::X")))}))
-      << Test.code();
+  std::vector<std::pair<llvm::StringRef, llvm::StringRef>> Tests{
+      {"incomplete_enum", "enum class X : int; using enum [[X]];"},
+      {"underlying_type_of_incomplete_enum",
+       "[[__underlying_type]](enum X) i;"},
+  };
+  for (auto Case : Tests) {
+    Annotations Main(Case.second);
+    TU.Code = Main.code().str() + "\n // error-ok";
+    EXPECT_THAT(*TU.build().getDiagnostics(),
+                Contains(AllOf(DiagName(Case.first), HasRange(Main.range()),
+                               WithFix(Fix(Range{}, "#include \"x.h\"\n",
+                                           "Include \"x.h\" for symbol X")))))
+        << Case.second;
+  }
 }
 
 TEST(IncludeFixerTest, NoSuggestIncludeWhenNoDefinitionInHeader) {
@@ -1097,8 +1085,6 @@
   )cpp");
   TestTU TU;
   TU.Code = std::string(Test.code());
-  // FIXME: Figure out why this is needed and remove it, PR43662.
-  TU.ExtraArgs.push_back("-fno-ms-compatibility");
   auto Index = buildIndexWithSymbol(
       SymbolWithHeader{"ns::scope::X_Y", "unittest:///x.h", "\"x.h\""});
   TU.ExternalIndex = Index.get();
@@ -1124,8 +1110,6 @@
   )cpp");
   TestTU TU;
   TU.Code = std::string(Test.code());
-  // FIXME: Figure out why this is needed and remove it, PR43662.
-  TU.ExtraArgs.push_back("-fno-ms-compatibility");
   auto Index = buildIndexWithSymbol(
       {SymbolWithHeader{"clang::clangd::X", "unittest:///x.h", "\"x.h\""},
        SymbolWithHeader{"clang::clangd::ns::Y", "unittest:///y.h", "\"y.h\""}});
Index: clang-tools-extra/clangd/IncludeFixer.cpp
===================================================================
--- clang-tools-extra/clangd/IncludeFixer.cpp
+++ clang-tools-extra/clangd/IncludeFixer.cpp
@@ -71,28 +71,115 @@
 std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
                                    const clang::Diagnostic &Info) const {
   switch (Info.getID()) {
-  case diag::err_incomplete_nested_name_spec:
+  /*
+   There are many "incomplete type" diagnostics!
+   They are almost all Sema diagnostics with "incomplete" in the name.
+
+   sed -n '/CLASS_NOTE/! s/DIAG(\\([^,]*\\).*)/  case diag::\\1:/p' \
+     tools/clang/include/clang/Basic/DiagnosticSemaKinds.inc | grep incomplete
+  */
+  // clang-format off
+  //case diag::err_alignof_member_of_incomplete_type:
+  case diag::err_array_incomplete_or_sizeless_type:
+  case diag::err_array_size_incomplete_type:
+  case diag::err_asm_incomplete_type:
+  case diag::err_assoc_type_incomplete:
+  case diag::err_bad_cast_incomplete:
+  case diag::err_call_function_incomplete_return:
+  case diag::err_call_incomplete_argument:
+  case diag::err_call_incomplete_return:
+  case diag::err_capture_of_incomplete_or_sizeless_type:
+  case diag::err_catch_incomplete:
+  case diag::err_catch_incomplete_ptr:
+  case diag::err_catch_incomplete_ref:
+  case diag::err_cconv_incomplete_param_type:
+  case diag::err_coroutine_promise_type_incomplete:
+  case diag::err_covariant_return_incomplete:
+  //case diag::err_deduced_class_template_incomplete:
+  case diag::err_delete_incomplete_class_type:
+  case diag::err_dereference_incomplete_type:
+  case diag::err_exception_spec_incomplete_type:
+  case diag::err_field_incomplete_or_sizeless:
+  case diag::err_for_range_incomplete_type:
+  case diag::err_func_def_incomplete_result:
+  case diag::err_ice_incomplete_type:
+  case diag::err_illegal_message_expr_incomplete_type:
   case diag::err_incomplete_base_class:
+  case diag::err_incomplete_enum:
+  case diag::err_incomplete_in_exception_spec:
   case diag::err_incomplete_member_access:
+  case diag::err_incomplete_nested_name_spec:
+  case diag::err_incomplete_object_call:
+  case diag::err_incomplete_receiver_type:
+  case diag::err_incomplete_synthesized_property:
   case diag::err_incomplete_type:
-  case diag::err_typecheck_decl_incomplete_type:
-  case diag::err_typecheck_incomplete_tag:
+  case diag::err_incomplete_type_objc_at_encode:
+  case diag::err_incomplete_type_used_in_type_trait_expr:
+  case diag::err_incomplete_typeid:
+  case diag::err_init_incomplete_type:
   case diag::err_invalid_incomplete_type_use:
+  case diag::err_lambda_incomplete_result:
+  //case diag::err_matrix_incomplete_index:
+  //case diag::err_matrix_separate_incomplete_index:
+  case diag::err_memptr_incomplete:
+  case diag::err_new_incomplete_or_sizeless_type:
+  case diag::err_objc_incomplete_boxed_expression_type:
+  case diag::err_objc_index_incomplete_class_type:
+  case diag::err_offsetof_incomplete_type:
+  case diag::err_omp_firstprivate_incomplete_type:
+  case diag::err_omp_incomplete_type:
+  case diag::err_omp_lastprivate_incomplete_type:
+  case diag::err_omp_linear_incomplete_type:
+  case diag::err_omp_private_incomplete_type:
+  case diag::err_omp_reduction_incomplete_type:
+  case diag::err_omp_section_incomplete_type:
+  case diag::err_omp_threadprivate_incomplete_type:
+  case diag::err_second_parameter_to_va_arg_incomplete:
   case diag::err_sizeof_alignof_incomplete_or_sizeless_type:
-  case diag::err_for_range_incomplete_type:
-  case diag::err_func_def_incomplete_result:
-  case diag::err_field_incomplete_or_sizeless:
+  case diag::err_subscript_incomplete_or_sizeless_type:
+  case diag::err_switch_incomplete_class_type:
+  case diag::err_temp_copy_incomplete:
+  //case diag::err_template_arg_deduced_incomplete_pack:
+  case diag::err_template_nontype_parm_incomplete:
+  //case diag::err_tentative_def_incomplete_type:
+  case diag::err_throw_incomplete:
+  case diag::err_throw_incomplete_ptr:
+  case diag::err_typecheck_arithmetic_incomplete_or_sizeless_type:
+  case diag::err_typecheck_cast_to_incomplete:
+  case diag::err_typecheck_decl_incomplete_type:
+  //case diag::err_typecheck_incomplete_array_needs_initializer:
+  case diag::err_typecheck_incomplete_tag:
+  case diag::err_typecheck_incomplete_type_not_modifiable_lvalue:
+  case diag::err_typecheck_nonviable_condition_incomplete:
+  case diag::err_underlying_type_of_incomplete_enum:
+  case diag::ext_incomplete_in_exception_spec:
+  //case diag::ext_typecheck_compare_complete_incomplete_pointers:
+  case diag::ext_typecheck_decl_incomplete_type:
+  case diag::warn_delete_incomplete:
+  case diag::warn_incomplete_encoded_type:
+  //case diag::warn_printf_incomplete_specifier:
+  case diag::warn_return_value_udt_incomplete:
+  //case diag::warn_scanf_scanlist_incomplete:
+  //case diag::warn_tentative_incomplete_array:
+    //  clang-format on
     // Incomplete type diagnostics should have a QualType argument for the
     // incomplete type.
     for (unsigned Idx = 0; Idx < Info.getNumArgs(); ++Idx) {
       if (Info.getArgKind(Idx) == DiagnosticsEngine::ak_qualtype) {
         auto QT = QualType::getFromOpaquePtr((void *)Info.getRawArg(Idx));
-        if (const Type *T = QT.getTypePtrOrNull())
+        if (const Type *T = QT.getTypePtrOrNull()) {
           if (T->isIncompleteType())
             return fixIncompleteType(*T);
+          // `enum x : int;' is not formally an incomplete type.
+          // We may need a full definition anyway.
+          if (auto * ET = llvm::dyn_cast<EnumType>(T))
+            if (ET->getDecl()->getDefinition() == nullptr)
+              return fixIncompleteType(*T);
+        }
       }
     }
     break;
+
   case diag::err_unknown_typename:
   case diag::err_unknown_typename_suggest:
   case diag::err_typename_nested_not_found:
@@ -123,6 +210,7 @@
         return fixUnresolvedName();
     }
     break;
+
   // Cases where clang explicitly knows which header to include.
   // (There's no fix provided for boring formatting reasons).
   case diag::err_implied_std_initializer_list_not_found:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to