Skip to content

Commit aa0fec1

Browse files
committed
[clang][Sema] Add flag to LookupName to force C/ObjC codepath
Motivation: The intent here is for use in Swift. When building a clang module for swift consumption, swift adds an extension block to the module for name lookup purposes. Swift calls this a SwiftLookupTable. One purpose that this serves is to handle conflicting names between ObjC classes and ObjC protocols. They exist in different namespaces in ObjC programs, but in Swift they would exist in the same namespace. Swift handles this by appending a suffix to a protocol name if it shares a name with a class. For example, if you have an ObjC class named "Foo" and a protocol with the same name, the protocol would be renamed to "FooProtocol" when imported into swift. When constructing the previously mentioned SwiftLookupTable, we use Sema::LookupName to look up name conflicts for the previous problem. By this time, the Parser has long finished its job so the call to LookupName gets nullptr for its Scope (TUScope will be nullptr by this point). The C/ObjC path does not have this problem because it only uses the Scope in specific scenarios. The C++ codepath uses the Scope quite extensively and will fail early on if the Scope it gets is null. In our very specific case of looking up ObjC classes with a specific name, we want to force sema::LookupName to take the C/ObjC codepath even if C++ or ObjC++ is enabled. (cherry picked from commit c57f034)
1 parent d428c6b commit aa0fec1

File tree

4 files changed

+66
-4
lines changed

4 files changed

+66
-4
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4149,8 +4149,8 @@ class Sema final {
41494149
= NotForRedeclaration);
41504150
bool LookupBuiltin(LookupResult &R);
41514151
void LookupNecessaryTypesForBuiltin(Scope *S, unsigned ID);
4152-
bool LookupName(LookupResult &R, Scope *S,
4153-
bool AllowBuiltinCreation = false);
4152+
bool LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation = false,
4153+
bool ForceNoCPlusPlus = false);
41544154
bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
41554155
bool InUnqualifiedLookup = false);
41564156
bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,

clang/lib/Sema/SemaLookup.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,13 +1911,14 @@ NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const {
19111911
/// used to diagnose ambiguities.
19121912
///
19131913
/// @returns \c true if lookup succeeded and false otherwise.
1914-
bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
1914+
bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation,
1915+
bool ForceNoCPlusPlus) {
19151916
DeclarationName Name = R.getLookupName();
19161917
if (!Name) return false;
19171918

19181919
LookupNameKind NameKind = R.getLookupKind();
19191920

1920-
if (!getLangOpts().CPlusPlus) {
1921+
if (!getLangOpts().CPlusPlus || ForceNoCPlusPlus) {
19211922
// Unqualified name lookup in C/Objective-C is purely lexical, so
19221923
// search in the declarations attached to the name.
19231924
if (NameKind == Sema::LookupRedeclarationWithLinkage) {

clang/unittests/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_clang_unittest(SemaTests
77
ExternalSemaSourceTest.cpp
88
CodeCompleteTest.cpp
99
GslOwnerPointerInference.cpp
10+
SemaLookupTest.cpp
1011
)
1112

1213
clang_target_link_libraries(SemaTests
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "clang/AST/DeclarationName.h"
2+
#include "clang/Frontend/CompilerInstance.h"
3+
#include "clang/Frontend/FrontendAction.h"
4+
#include "clang/Parse/ParseAST.h"
5+
#include "clang/Sema/Lookup.h"
6+
#include "clang/Sema/Sema.h"
7+
#include "clang/Tooling/Tooling.h"
8+
#include "gtest/gtest.h"
9+
10+
using namespace llvm;
11+
using namespace clang;
12+
using namespace clang::tooling;
13+
14+
namespace {
15+
16+
class LookupAction : public ASTFrontendAction {
17+
std::unique_ptr<ASTConsumer>
18+
CreateASTConsumer(CompilerInstance &CI, StringRef /*Unused*/) override {
19+
return std::make_unique<clang::ASTConsumer>();
20+
}
21+
22+
void ExecuteAction() override {
23+
CompilerInstance &CI = getCompilerInstance();
24+
ASSERT_FALSE(CI.hasSema());
25+
CI.createSema(getTranslationUnitKind(), nullptr);
26+
ASSERT_TRUE(CI.hasSema());
27+
Sema &S = CI.getSema();
28+
ParseAST(S);
29+
30+
ASTContext &Ctx = S.getASTContext();
31+
auto Name = &Ctx.Idents.get("Foo");
32+
LookupResult R_cpp(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
33+
S.LookupName(R_cpp, S.TUScope, /*AllowBuiltinCreation=*/false,
34+
/*ForceNoCPlusPlus=*/false);
35+
// By this point, parsing is done and S.TUScope is nullptr
36+
// CppLookupName will perform an early return with no results if the Scope
37+
// we pass in is nullptr. We expect to find nothing.
38+
ASSERT_TRUE(R_cpp.empty());
39+
40+
// On the other hand, the non-C++ path doesn't care if the Scope passed in
41+
// is nullptr. We'll force the non-C++ path with a flag.
42+
LookupResult R_nocpp(S, Name, SourceLocation(), Sema::LookupOrdinaryName);
43+
S.LookupName(R_nocpp, S.TUScope, /*AllowBuiltinCreation=*/false,
44+
/*ForceNoCPlusPlus=*/true);
45+
ASSERT_TRUE(!R_nocpp.empty());
46+
}
47+
};
48+
49+
TEST(SemaLookupTest, ForceNoCPlusPlusPath) {
50+
const char *file_contents = R"objcxx(
51+
@protocol Foo
52+
@end
53+
@interface Foo <Foo>
54+
@end
55+
)objcxx";
56+
ASSERT_TRUE(runToolOnCodeWithArgs(std::make_unique<LookupAction>(),
57+
file_contents, {"-x", "objective-c++"},
58+
"test.mm"));
59+
}
60+
} // namespace

0 commit comments

Comments
 (0)