From c3f2d2bb839593eed8861bce14228df0faf7b6c0 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 17 Feb 2010 02:12:47 +0000 Subject: [PATCH] Implement AST importing and checking for Objective-C method declarations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96442 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTImporter.h | 6 + include/clang/Basic/DiagnosticASTKinds.td | 15 ++- lib/AST/ASTImporter.cpp | 144 ++++++++++++++++++++-- test/ASTMerge/Inputs/interface1.m | 24 ++++ test/ASTMerge/Inputs/interface2.m | 23 ++++ test/ASTMerge/interface.m | 8 +- 6 files changed, 210 insertions(+), 10 deletions(-) diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h index f5f11ca836..7975c43788 100644 --- a/include/clang/AST/ASTImporter.h +++ b/include/clang/AST/ASTImporter.h @@ -156,6 +156,12 @@ namespace clang { /// \returns the equivalent identifier in the "to" context. IdentifierInfo *Import(IdentifierInfo *FromId); + /// \brief Import the given Objective-C selector from the "from" + /// context into the "to" context. + /// + /// \returns the equivalent selector in the "to" context. + Selector Import(Selector FromSel); + /// \brief Import the given file ID from the "from" context into the /// "to" context. /// diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index a435e35e5a..62d46abd74 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -60,6 +60,19 @@ def err_odr_objc_superclass_inconsistent : Error< "class %0 has incompatible superclasses">; def note_odr_objc_superclass : Note<"inherits from superclass %0 here">; def note_odr_objc_missing_superclass : Note<"no corresponding superclass here">; - +def err_odr_objc_method_result_type_inconsistent : Error< + "%select{class|instance}0 method %1 has incompatible result types in " + "different translation units (%2 vs. %3)">; +def err_odr_objc_method_num_params_inconsistent : Error< + "%select{class|instance}0 method %1 has a different number of parameters in " + "different translation units (%2 vs. %3)">; +def err_odr_objc_method_param_type_inconsistent : Error< + "%select{class|instance}0 method %1 has a parameter with a different types " + "in different translation units (%2 vs. %3)">; +def err_odr_objc_method_variadic_inconsistent : Error< + "%select{class|instance}0 method %1 is variadic in one translation unit " + "and not variadic in another">; +def note_odr_objc_method_here : Note< + "%select{class|instance}0 method %1 also declared here">; def err_unsupported_ast_node: Error<"cannot import unsupported AST node %0">; } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index ff047829c8..a6e565d507 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -93,6 +93,7 @@ namespace { Decl *VisitObjCIvarDecl(ObjCIvarDecl *D); Decl *VisitVarDecl(VarDecl *D); Decl *VisitParmVarDecl(ParmVarDecl *D); + Decl *VisitObjCMethodDecl(ObjCMethodDecl *D); Decl *VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); // Importing statements @@ -1774,25 +1775,25 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { // Create the imported function. TypeSourceInfo *TInfo = Importer.Import(D->getTypeSourceInfo()); - FunctionDecl *ToEnumerator + FunctionDecl *ToFunction = FunctionDecl::Create(Importer.getToContext(), DC, Loc, Name, T, TInfo, D->getStorageClass(), D->isInlineSpecified(), D->hasWrittenPrototype()); - ToEnumerator->setLexicalDeclContext(LexicalDC); - Importer.Imported(D, ToEnumerator); - LexicalDC->addDecl(ToEnumerator); + ToFunction->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToFunction); + LexicalDC->addDecl(ToFunction); // Set the parameters. for (unsigned I = 0, N = Parameters.size(); I != N; ++I) { - Parameters[I]->setOwningFunction(ToEnumerator); - ToEnumerator->addDecl(Parameters[I]); + Parameters[I]->setOwningFunction(ToFunction); + ToFunction->addDecl(Parameters[I]); } - ToEnumerator->setParams(Parameters.data(), Parameters.size()); + ToFunction->setParams(Parameters.data(), Parameters.size()); // FIXME: Other bits to merge? - return ToEnumerator; + return ToFunction; } Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { @@ -2013,6 +2014,122 @@ Decl *ASTNodeImporter::VisitParmVarDecl(ParmVarDecl *D) { return Importer.Imported(D, ToParm); } +Decl *ASTNodeImporter::VisitObjCMethodDecl(ObjCMethodDecl *D) { + // Import the major distinguishing characteristics of a method. + DeclContext *DC, *LexicalDC; + DeclarationName Name; + SourceLocation Loc; + if (ImportDeclParts(D, DC, LexicalDC, Name, Loc)) + return 0; + + for (DeclContext::lookup_result Lookup = DC->lookup(Name); + Lookup.first != Lookup.second; + ++Lookup.first) { + if (ObjCMethodDecl *FoundMethod = dyn_cast(*Lookup.first)) { + if (FoundMethod->isInstanceMethod() != D->isInstanceMethod()) + continue; + + // Check return types. + if (!Importer.IsStructurallyEquivalent(D->getResultType(), + FoundMethod->getResultType())) { + Importer.ToDiag(Loc, diag::err_odr_objc_method_result_type_inconsistent) + << D->isInstanceMethod() << Name + << D->getResultType() << FoundMethod->getResultType(); + Importer.ToDiag(FoundMethod->getLocation(), + diag::note_odr_objc_method_here) + << D->isInstanceMethod() << Name; + return 0; + } + + // Check the number of parameters. + if (D->param_size() != FoundMethod->param_size()) { + Importer.ToDiag(Loc, diag::err_odr_objc_method_num_params_inconsistent) + << D->isInstanceMethod() << Name + << D->param_size() << FoundMethod->param_size(); + Importer.ToDiag(FoundMethod->getLocation(), + diag::note_odr_objc_method_here) + << D->isInstanceMethod() << Name; + return 0; + } + + // Check parameter types. + for (ObjCMethodDecl::param_iterator P = D->param_begin(), + PEnd = D->param_end(), FoundP = FoundMethod->param_begin(); + P != PEnd; ++P, ++FoundP) { + if (!Importer.IsStructurallyEquivalent((*P)->getType(), + (*FoundP)->getType())) { + Importer.FromDiag((*P)->getLocation(), + diag::err_odr_objc_method_param_type_inconsistent) + << D->isInstanceMethod() << Name + << (*P)->getType() << (*FoundP)->getType(); + Importer.ToDiag((*FoundP)->getLocation(), diag::note_odr_value_here) + << (*FoundP)->getType(); + return 0; + } + } + + // Check variadic/non-variadic. + // Check the number of parameters. + if (D->isVariadic() != FoundMethod->isVariadic()) { + Importer.ToDiag(Loc, diag::err_odr_objc_method_variadic_inconsistent) + << D->isInstanceMethod() << Name; + Importer.ToDiag(FoundMethod->getLocation(), + diag::note_odr_objc_method_here) + << D->isInstanceMethod() << Name; + return 0; + } + + // FIXME: Any other bits we need to merge? + return Importer.Imported(D, FoundMethod); + } + } + + // Import the result type. + QualType ResultTy = Importer.Import(D->getResultType()); + if (ResultTy.isNull()) + return 0; + + ObjCMethodDecl *ToMethod + = ObjCMethodDecl::Create(Importer.getToContext(), + Loc, + Importer.Import(D->getLocEnd()), + Name.getObjCSelector(), + ResultTy, DC, + D->isInstanceMethod(), + D->isVariadic(), + D->isSynthesized(), + D->getImplementationControl()); + + // FIXME: When we decide to merge method definitions, we'll need to + // deal with implicit parameters. + + // Import the parameters + llvm::SmallVector ToParams; + for (ObjCMethodDecl::param_iterator FromP = D->param_begin(), + FromPEnd = D->param_end(); + FromP != FromPEnd; + ++FromP) { + ParmVarDecl *ToP = cast_or_null(Importer.Import(*FromP)); + if (!ToP) + return 0; + + ToParams.push_back(ToP); + } + + // Set the parameters. + for (unsigned I = 0, N = ToParams.size(); I != N; ++I) { + ToParams[I]->setOwningFunction(ToMethod); + ToMethod->addDecl(ToParams[I]); + } + ToMethod->setMethodParams(Importer.getToContext(), + ToParams.data(), ToParams.size()); + + ToMethod->setLexicalDeclContext(LexicalDC); + Importer.Imported(D, ToMethod); + LexicalDC->addDecl(ToMethod); + return ToMethod; +} + Decl *ASTNodeImporter::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { // Import the major distinguishing characteristics of an @interface. DeclContext *DC, *LexicalDC; @@ -2432,6 +2549,17 @@ IdentifierInfo *ASTImporter::Import(IdentifierInfo *FromId) { return &ToContext.Idents.get(FromId->getName()); } +Selector ASTImporter::Import(Selector FromSel) { + if (FromSel.isNull()) + return Selector(); + + llvm::SmallVector Idents; + Idents.push_back(Import(FromSel.getIdentifierInfoForSlot(0))); + for (unsigned I = 1, N = FromSel.getNumArgs(); I < N; ++I) + Idents.push_back(Import(FromSel.getIdentifierInfoForSlot(I))); + return ToContext.Selectors.getSelector(FromSel.getNumArgs(), Idents.data()); +} + DeclarationName ASTImporter::HandleNameConflict(DeclarationName Name, DeclContext *DC, unsigned IDNS, diff --git a/test/ASTMerge/Inputs/interface1.m b/test/ASTMerge/Inputs/interface1.m index ebcd2bbbd9..bde667475d 100644 --- a/test/ASTMerge/Inputs/interface1.m +++ b/test/ASTMerge/Inputs/interface1.m @@ -21,3 +21,27 @@ @interface I4 : I2 { } @end + +// Methods match +@interface I5 +- (int)foo; ++ (float)bar; +@end + +// Method mismatch +@interface I6 +- (int)foo; ++ (int)foo; +@end + +// Method mismatch +@interface I7 +- (int)foo; ++ (int)bar:(int)x; +@end + +// Method mismatch +@interface I8 +- (int)foo; ++ (int)bar:(float)x; +@end diff --git a/test/ASTMerge/Inputs/interface2.m b/test/ASTMerge/Inputs/interface2.m index 2e6b0bf2b4..1d5bebd996 100644 --- a/test/ASTMerge/Inputs/interface2.m +++ b/test/ASTMerge/Inputs/interface2.m @@ -21,3 +21,26 @@ @interface I4 : I1 { } @end + +// Methods match +@interface I5 ++ (float)bar; +- (int)foo; +@end + +// Method mismatch +@interface I6 ++ (float)foo; +@end + +// Method mismatch +@interface I7 +- (int)foo; ++ (int)bar:(float)x; +@end + +// Method mismatch +@interface I8 +- (int)foo; ++ (int)bar:(float)x, ...; +@end diff --git a/test/ASTMerge/interface.m b/test/ASTMerge/interface.m index ced3fc8981..465077b420 100644 --- a/test/ASTMerge/interface.m +++ b/test/ASTMerge/interface.m @@ -7,5 +7,11 @@ // CHECK: interface1.m:21:1: error: class 'I4' has incompatible superclasses // CHECK: interface1.m:21:17: note: inherits from superclass 'I2' here // CHECK: interface2.m:21:17: note: inherits from superclass 'I1' here -// CHECK: 5 diagnostics generated +// CHECK: interface2.m:33:1: error: class method 'foo' has incompatible result types in different translation units ('float' vs. 'int') +// CHECK: interface1.m:34:1: note: class method 'foo' also declared here +// CHECK: interface2.m:39:19: error: class method 'bar:' has a parameter with a different types in different translation units ('float' vs. 'int') +// CHECK: interface1.m:40:17: note: declared here with type 'int' +// CHECK: interface2.m:45:1: error: class method 'bar:' is variadic in one translation unit and not variadic in another +// CHECK: interface1.m:46:1: note: class method 'bar:' also declared here +// CHECK: 11 diagnostics generated -- 2.40.0