diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -71,28 +71,115 @@ std::vector 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(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 @@ 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 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -843,14 +843,29 @@ {"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]](0);"}, + {"template_nontype_parm_incomplete", "template 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, 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> 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.h b/clang-tools-extra/clangd/unittests/TestIndex.h --- 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 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. diff --git a/clang-tools-extra/clangd/unittests/TestIndex.cpp b/clang-tools-extra/clangd/unittests/TestIndex.cpp --- a/clang-tools-extra/clangd/unittests/TestIndex.cpp +++ b/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"); }