From f7500a4ef7bd7820b42e1b934392bb2566083b27 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 9 Nov 2021 18:12:01 +0100 Subject: [PATCH] [CodeCompletion] Generally consider header files without extension Real-world use case: The Qt framework's headers have the same name as the respective class defined in them, and Qt's traditional qmake build tool uses -I (rather than -isystem) to pull them in. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D112996 --- clang/lib/Sema/SemaCodeComplete.cpp | 25 ++++++++------ clang/test/CodeCompletion/included-files.cpp | 36 ++++++++++++++++---- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 2186eec955bf..26e982686cfb 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -9613,6 +9613,10 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { } } + const StringRef &Dirname = llvm::sys::path::filename(Dir); + const bool isQt = Dirname.startswith("Qt") || Dirname == "ActiveQt"; + const bool ExtensionlessHeaders = + IsSystem || isQt || Dir.endswith(".framework/Headers"); std::error_code EC; unsigned Count = 0; for (auto It = FS.dir_begin(Dir, EC); @@ -9639,18 +9643,19 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) { AddCompletion(Filename, /*IsDirectory=*/true); break; - case llvm::sys::fs::file_type::regular_file: - // Only files that really look like headers. (Except in system dirs). - if (!IsSystem) { - // Header extensions from Types.def, which we can't depend on here. - if (!(Filename.endswith_insensitive(".h") || - Filename.endswith_insensitive(".hh") || - Filename.endswith_insensitive(".hpp") || - Filename.endswith_insensitive(".inc"))) - break; - } + case llvm::sys::fs::file_type::regular_file: { + // Only files that really look like headers. (Except in special dirs). + // Header extensions from Types.def, which we can't depend on here. + const bool IsHeader = Filename.endswith_insensitive(".h") || + Filename.endswith_insensitive(".hh") || + Filename.endswith_insensitive(".hpp") || + Filename.endswith_insensitive(".inc") || + (ExtensionlessHeaders && !Filename.contains('.')); + if (!IsHeader) + break; AddCompletion(Filename, /*IsDirectory=*/false); break; + } default: break; } diff --git a/clang/test/CodeCompletion/included-files.cpp b/clang/test/CodeCompletion/included-files.cpp index ba153e6e2754..ae5c3cff9378 100644 --- a/clang/test/CodeCompletion/included-files.cpp +++ b/clang/test/CodeCompletion/included-files.cpp @@ -1,35 +1,57 @@ -// RUN: rm -rf %t && mkdir %t && cp %s %t/main.cc && mkdir %t/a -// RUN: touch %t/foo.h && touch %t/foo.cc && touch %t/a/foosys %t/a/foosys.h +// RUN: rm -rf %t && mkdir %t && cp %s %t/main.cc && mkdir %t/a && mkdir %t/QtCore && mkdir %t/Headers %t/Some.framework %t/Some.framework/Headers +// RUN: touch %t/foo.h && touch %t/foo.cc && touch %t/a/foosys %t/a/foosys.h && touch %t/QtCore/foosys %t/QtCore/foo.h +// RUN: touch %t/Headers/foosys %t/Headers/foo.h %t/Some.framework/Headers/foosys %t/Some.framework/Headers/foo.h // Quoted string shows header-ish files from CWD, and all from system. #include "foo.h" -// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:5:13 %t/main.cc | FileCheck -check-prefix=CHECK-1 %s +// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:6:13 %t/main.cc | FileCheck -check-prefix=CHECK-1 %s // CHECK-1-NOT: foo.cc" // CHECK-1: foo.h" // CHECK-1: foosys" // Quoted string with dir shows header-ish files in that subdir. #include "a/foosys" -// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:12:13 %t/main.cc | FileCheck -check-prefix=CHECK-2 %s +// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:13:13 %t/main.cc | FileCheck -check-prefix=CHECK-2 %s // CHECK-2-NOT: foo.h" // CHECK-2: foosys.h" // CHECK-2-NOT: foosys" // Angled shows headers from system dirs. #include -// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:19:13 %t/main.cc | FileCheck -check-prefix=CHECK-3 %s +// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:20:13 %t/main.cc | FileCheck -check-prefix=CHECK-3 %s // CHECK-3-NOT: foo.cc> // CHECK-3-NOT: foo.h> // CHECK-3: foosys> // With -I rather than -isystem, the header extension is required. #include -// RUN: %clang -fsyntax-only -I %t/a -Xclang -code-completion-at=%t/main.cc:26:13 %t/main.cc | FileCheck -check-prefix=CHECK-4 %s +// RUN: %clang -fsyntax-only -I %t/a -Xclang -code-completion-at=%t/main.cc:27:13 %t/main.cc | FileCheck -check-prefix=CHECK-4 %s // CHECK-4-NOT: foo.cc> // CHECK-4-NOT: foo.h> // CHECK-4-NOT: foosys> // Backslash handling. #include "a\foosys" -// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:33:13 %t/main.cc -fms-compatibility | FileCheck -check-prefix=CHECK-5 %s +// RUN: %clang -fsyntax-only -isystem %t/a -Xclang -code-completion-at=%t/main.cc:34:13 %t/main.cc -fms-compatibility | FileCheck -check-prefix=CHECK-5 %s // CHECK-5: foosys.h" + +// Qt headers don't necessarily have extensions. +#include +// RUN: %clang -fsyntax-only -I %t/QtCore -Xclang -code-completion-at=%t/main.cc:39:13 %t/main.cc -fms-compatibility | FileCheck -check-prefix=CHECK-6 %s +// CHECK-6-NOT: foo.cc> +// CHECK-6: foo.h> +// CHECK-6: foosys> + +// If the include path directly points into a framework's Headers/ directory, we allow extension-less headers. +#include +// RUN: %clang -fsyntax-only -I %t/Some.framework/Headers -Xclang -code-completion-at=%t/main.cc:46:13 %t/main.cc -fms-compatibility | FileCheck -check-prefix=CHECK-7 %s +// CHECK-7-NOT: foo.cc> +// CHECK-7: foo.h> +// CHECK-7: foosys> + +// Simply naming a directory "Headers" is not enough to allow extension-less headers. +#include +// RUN: %clang -fsyntax-only -I %t/Headers -Xclang -code-completion-at=%t/main.cc:53:13 %t/main.cc -fms-compatibility | FileCheck -check-prefix=CHECK-8 %s +// CHECK-8-NOT: foo.cc> +// CHECK-8: foo.h> +// CHECK-8-NOT: foosys>