[clang-tools-extra] c25ea48 - [clangd] Include-fixer: handle more "incomplete type" diags.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Fri Dec 10 16:46:42 PST 2021


Author: Sam McCall
Date: 2021-12-11T01:46:35+01:00
New Revision: c25ea488a39a090d43c0fd6dd68fdc26dc563708

URL: https://github.com/llvm/llvm-project/commit/c25ea488a39a090d43c0fd6dd68fdc26dc563708
DIFF: https://github.com/llvm/llvm-project/commit/c25ea488a39a090d43c0fd6dd68fdc26dc563708.diff

LOG: [clangd] Include-fixer: handle more "incomplete type" diags.

I started adding tests for all diags but found there were just too many cases.

Differential Revision: https://reviews.llvm.org/D115484

Added: 
    

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp
index 24d9b4918543..8c020f2486f0 100644
--- a/clang-tools-extra/clangd/IncludeFixer.cpp
+++ b/clang-tools-extra/clangd/IncludeFixer.cpp
@@ -71,28 +71,115 @@ std::vector<Fix> only(llvm::Optional<Fix> F) {
 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())
+              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 @@ std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
         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:

diff  --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
index c46f084c4ebc..c28efdf014e5 100644
--- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
@@ -843,14 +843,29 @@ TEST(IncludeFixerTest, IncompleteType) {
       {"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);
@@ -864,6 +879,36 @@ TEST(IncludeFixerTest, IncompleteType) {
   }
 }
 
+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());
+
+  TestTU TU;
+  TU.ExternalIndex = Index.get();
+  TU.ExtraArgs.push_back("-std=c++20");
+
+  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) {
   Annotations Test(R"cpp(// error-ok
 $insert[[]]namespace ns {

diff  --git a/clang-tools-extra/clangd/unittests/TestIndex.cpp b/clang-tools-extra/clangd/unittests/TestIndex.cpp
index 04fcbb46a89c..62e4fd684cfd 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.cpp
+++ b/clang-tools-extra/clangd/unittests/TestIndex.cpp
@@ -65,6 +65,10 @@ Symbol cls(llvm::StringRef Name) {
   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");
 }

diff  --git a/clang-tools-extra/clangd/unittests/TestIndex.h b/clang-tools-extra/clangd/unittests/TestIndex.h
index f83e16e5314f..76f88cd117ac 100644
--- a/clang-tools-extra/clangd/unittests/TestIndex.h
+++ b/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 sym(llvm::StringRef QName, index::SymbolKind Kind,
 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.


        


More information about the cfe-commits mailing list