diff options
Diffstat (limited to 'clang/lib/Sema/SemaDeclObjC.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDeclObjC.cpp | 549 |
1 files changed, 468 insertions, 81 deletions
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 574a7c8857c..ac1e3923afa 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -464,6 +464,142 @@ static void diagnoseUseOfProtocols(Sema &TheSema, } } +void Sema:: +ActOnSuperClassOfClassInterface(Scope *S, + SourceLocation AtInterfaceLoc, + ObjCInterfaceDecl *IDecl, + IdentifierInfo *ClassName, + SourceLocation ClassLoc, + IdentifierInfo *SuperName, + SourceLocation SuperLoc, + ArrayRef<ParsedType> SuperTypeArgs, + SourceRange SuperTypeArgsRange) { + // Check if a different kind of symbol declared in this scope. + NamedDecl *PrevDecl = LookupSingleName(TUScope, SuperName, SuperLoc, + LookupOrdinaryName); + + if (!PrevDecl) { + // Try to correct for a typo in the superclass name without correcting + // to the class we're defining. + if (TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(SuperName, SuperLoc), + LookupOrdinaryName, TUScope, + NULL, llvm::make_unique<ObjCInterfaceValidatorCCC>(IDecl), + CTK_ErrorRecovery)) { + diagnoseTypo(Corrected, PDiag(diag::err_undef_superclass_suggest) + << SuperName << ClassName); + PrevDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>(); + } + } + + if (declaresSameEntity(PrevDecl, IDecl)) { + Diag(SuperLoc, diag::err_recursive_superclass) + << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); + IDecl->setEndOfDefinitionLoc(ClassLoc); + } else { + ObjCInterfaceDecl *SuperClassDecl = + dyn_cast_or_null<ObjCInterfaceDecl>(PrevDecl); + QualType SuperClassType; + + // Diagnose classes that inherit from deprecated classes. + if (SuperClassDecl) { + (void)DiagnoseUseOfDecl(SuperClassDecl, SuperLoc); + SuperClassType = Context.getObjCInterfaceType(SuperClassDecl); + } + + if (PrevDecl && SuperClassDecl == 0) { + // The previous declaration was not a class decl. Check if we have a + // typedef. If we do, get the underlying class type. + if (const TypedefNameDecl *TDecl = + dyn_cast_or_null<TypedefNameDecl>(PrevDecl)) { + QualType T = TDecl->getUnderlyingType(); + if (T->isObjCObjectType()) { + if (NamedDecl *IDecl = T->getAs<ObjCObjectType>()->getInterface()) { + SuperClassDecl = dyn_cast<ObjCInterfaceDecl>(IDecl); + SuperClassType = Context.getTypeDeclType(TDecl); + + // This handles the following case: + // @interface NewI @end + // typedef NewI DeprI __attribute__((deprecated("blah"))) + // @interface SI : DeprI /* warn here */ @end + (void)DiagnoseUseOfDecl(const_cast<TypedefNameDecl*>(TDecl), SuperLoc); + } + } + } + + // This handles the following case: + // + // typedef int SuperClass; + // @interface MyClass : SuperClass {} @end + // + if (!SuperClassDecl) { + Diag(SuperLoc, diag::err_redefinition_different_kind) << SuperName; + Diag(PrevDecl->getLocation(), diag::note_previous_definition); + } + } + + if (!dyn_cast_or_null<TypedefNameDecl>(PrevDecl)) { + if (!SuperClassDecl) + Diag(SuperLoc, diag::err_undef_superclass) + << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); + else if (RequireCompleteType(SuperLoc, + SuperClassType, + diag::err_forward_superclass, + SuperClassDecl->getDeclName(), + ClassName, + SourceRange(AtInterfaceLoc, ClassLoc))) { + SuperClassDecl = 0; + SuperClassType = QualType(); + } + } + + if (SuperClassType.isNull()) { + assert(!SuperClassDecl && "Failed to set SuperClassType?"); + return; + } + + // Handle type arguments on the superclass. + TypeSourceInfo *SuperClassTInfo = nullptr; + if (!SuperTypeArgs.empty()) { + // Form declaration specifiers naming this superclass type with + // type arguments. + AttributeFactory attrFactory; + DeclSpec DS(attrFactory); + const char* prevSpec; // unused + unsigned diagID; // unused + TypeSourceInfo *parsedTSInfo + = Context.getTrivialTypeSourceInfo(SuperClassType, SuperLoc); + ParsedType parsedType = CreateParsedType(SuperClassType, parsedTSInfo); + + DS.SetTypeSpecType(DeclSpec::TST_typename, SuperLoc, prevSpec, diagID, + parsedType, Context.getPrintingPolicy()); + DS.SetRangeStart(SuperLoc); + DS.SetRangeEnd(SuperLoc); + DS.setObjCTypeArgs(SuperTypeArgsRange.getBegin(), + SuperTypeArgs, + SuperTypeArgsRange.getEnd()); + + // Form the declarator. + Declarator D(DS, Declarator::TypeNameContext); + + TypeResult fullSuperClassType = ActOnTypeName(S, D); + if (!fullSuperClassType.isUsable()) + return; + + SuperClassType = GetTypeFromParser(fullSuperClassType.get(), + &SuperClassTInfo); + } + + if (!SuperClassTInfo) { + SuperClassTInfo = Context.getTrivialTypeSourceInfo(SuperClassType, + SuperLoc); + } + + IDecl->setSuperClass(SuperClassTInfo); + IDecl->setEndOfDefinitionLoc(SuperClassTInfo->getTypeLoc().getLocEnd()); + } +} + DeclResult Sema::actOnObjCTypeParam(Scope *S, IdentifierInfo *paramName, SourceLocation paramLoc, SourceLocation colonLoc, @@ -499,7 +635,7 @@ DeclResult Sema::actOnObjCTypeParam(Scope *S, IdentifierInfo *paramName, // Form the new type source information. typeBoundInfo = builder.getTypeSourceInfo(Context, typeBound); } else { - // Not a + // Not a valid type bound. Diag(typeBoundInfo->getTypeLoc().getBeginLoc(), diag::err_objc_type_param_bound_nonobject) << typeBound << paramName; @@ -669,10 +805,12 @@ static bool checkTypeParamListConsistency(Sema &S, } Decl *Sema:: -ActOnStartClassInterface(SourceLocation AtInterfaceLoc, +ActOnStartClassInterface(Scope *S, SourceLocation AtInterfaceLoc, IdentifierInfo *ClassName, SourceLocation ClassLoc, ObjCTypeParamList *typeParamList, IdentifierInfo *SuperName, SourceLocation SuperLoc, + ArrayRef<ParsedType> SuperTypeArgs, + SourceRange SuperTypeArgsRange, Decl * const *ProtoRefs, unsigned NumProtoRefs, const SourceLocation *ProtoLocs, SourceLocation EndProtoLoc, AttributeList *AttrList) { @@ -767,84 +905,13 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, IDecl->startDefinition(); if (SuperName) { - // Check if a different kind of symbol declared in this scope. - PrevDecl = LookupSingleName(TUScope, SuperName, SuperLoc, - LookupOrdinaryName); - - if (!PrevDecl) { - // Try to correct for a typo in the superclass name without correcting - // to the class we're defining. - if (TypoCorrection Corrected = - CorrectTypo(DeclarationNameInfo(SuperName, SuperLoc), - LookupOrdinaryName, TUScope, nullptr, - llvm::make_unique<ObjCInterfaceValidatorCCC>(IDecl), - CTK_ErrorRecovery)) { - diagnoseTypo(Corrected, PDiag(diag::err_undef_superclass_suggest) - << SuperName << ClassName); - PrevDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>(); - } - } - - if (declaresSameEntity(PrevDecl, IDecl)) { - Diag(SuperLoc, diag::err_recursive_superclass) - << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); - IDecl->setEndOfDefinitionLoc(ClassLoc); - } else { - ObjCInterfaceDecl *SuperClassDecl = - dyn_cast_or_null<ObjCInterfaceDecl>(PrevDecl); - - // Diagnose availability in the context of the @interface. - ContextRAII SavedContext(*this, IDecl); - // Diagnose classes that inherit from deprecated classes. - if (SuperClassDecl) - (void)DiagnoseUseOfDecl(SuperClassDecl, SuperLoc); - - if (PrevDecl && !SuperClassDecl) { - // The previous declaration was not a class decl. Check if we have a - // typedef. If we do, get the underlying class type. - if (const TypedefNameDecl *TDecl = - dyn_cast_or_null<TypedefNameDecl>(PrevDecl)) { - QualType T = TDecl->getUnderlyingType(); - if (T->isObjCObjectType()) { - if (NamedDecl *IDecl = T->getAs<ObjCObjectType>()->getInterface()) { - SuperClassDecl = dyn_cast<ObjCInterfaceDecl>(IDecl); - // This handles the following case: - // @interface NewI @end - // typedef NewI DeprI __attribute__((deprecated("blah"))) - // @interface SI : DeprI /* warn here */ @end - (void)DiagnoseUseOfDecl(const_cast<TypedefNameDecl*>(TDecl), SuperLoc); - } - } - } - - // This handles the following case: - // - // typedef int SuperClass; - // @interface MyClass : SuperClass {} @end - // - if (!SuperClassDecl) { - Diag(SuperLoc, diag::err_redefinition_different_kind) << SuperName; - Diag(PrevDecl->getLocation(), diag::note_previous_definition); - } - } + // Diagnose availability in the context of the @interface. + ContextRAII SavedContext(*this, IDecl); - if (!dyn_cast_or_null<TypedefNameDecl>(PrevDecl)) { - if (!SuperClassDecl) - Diag(SuperLoc, diag::err_undef_superclass) - << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); - else if (RequireCompleteType(SuperLoc, - Context.getObjCInterfaceType(SuperClassDecl), - diag::err_forward_superclass, - SuperClassDecl->getDeclName(), - ClassName, - SourceRange(AtInterfaceLoc, ClassLoc))) { - SuperClassDecl = nullptr; - } - } - IDecl->setSuperClass(SuperClassDecl); - IDecl->setSuperClassLoc(SuperLoc); - IDecl->setEndOfDefinitionLoc(SuperLoc); - } + ActOnSuperClassOfClassInterface(S, AtInterfaceLoc, IDecl, + ClassName, ClassLoc, + SuperName, SuperLoc, SuperTypeArgs, + SuperTypeArgsRange); } else { // we have a root class. IDecl->setEndOfDefinitionLoc(ClassLoc); } @@ -1091,6 +1158,325 @@ Sema::FindProtocolDeclaration(bool WarnOnDeclarations, bool ForObjCContainer, } } +// Callback to only accept typo corrections that are either +// Objective-C protocols or valid Objective-C type arguments. +class ObjCTypeArgOrProtocolValidatorCCC : public CorrectionCandidateCallback { + ASTContext &Context; + Sema::LookupNameKind LookupKind; + public: + ObjCTypeArgOrProtocolValidatorCCC(ASTContext &context, + Sema::LookupNameKind lookupKind) + : Context(context), LookupKind(lookupKind) { } + + bool ValidateCandidate(const TypoCorrection &candidate) override { + // If we're allowed to find protocols and we have a protocol, accept it. + if (LookupKind != Sema::LookupOrdinaryName) { + if (candidate.getCorrectionDeclAs<ObjCProtocolDecl>()) + return true; + } + + // If we're allowed to find type names and we have one, accept it. + if (LookupKind != Sema::LookupObjCProtocolName) { + // If we have a type declaration, we might accept this result. + if (auto typeDecl = candidate.getCorrectionDeclAs<TypeDecl>()) { + // If we found a tag declaration outside of C++, skip it. This + // can happy because we look for any name when there is no + // bias to protocol or type names. + if (isa<RecordDecl>(typeDecl) && !Context.getLangOpts().CPlusPlus) + return false; + + // Make sure the type is something we would accept as a type + // argument. + auto type = Context.getTypeDeclType(typeDecl); + if (type->isObjCObjectPointerType() || + type->isBlockPointerType() || + type->isDependentType() || + type->isObjCObjectType()) + return true; + + return false; + } + + // If we have an Objective-C class type, accept it; there will + // be another fix to add the '*'. + if (candidate.getCorrectionDeclAs<ObjCInterfaceDecl>()) + return true; + + return false; + } + + return false; + } +}; + +void Sema::actOnObjCTypeArgsOrProtocolQualifiers( + Scope *S, + DeclSpec &DS, + SourceLocation lAngleLoc, + ArrayRef<IdentifierInfo *> identifiers, + ArrayRef<SourceLocation> identifierLocs, + SourceLocation rAngleLoc, + bool warnOnIncompleteProtocols) { + // Local function that updates the declaration specifiers with + // protocol information. + SmallVector<ObjCProtocolDecl *, 4> protocols; + unsigned numProtocolsResolved = 0; + auto resolvedAsProtocols = [&] { + assert(numProtocolsResolved == identifiers.size() && "Unresolved protocols"); + + for (unsigned i = 0, n = protocols.size(); i != n; ++i) { + ObjCProtocolDecl *&proto = protocols[i]; + // For an objc container, delay protocol reference checking until after we + // can set the objc decl as the availability context, otherwise check now. + if (!warnOnIncompleteProtocols) { + (void)DiagnoseUseOfDecl(proto, identifierLocs[i]); + } + + // If this is a forward protocol declaration, get its definition. + if (!proto->isThisDeclarationADefinition() && proto->getDefinition()) + proto = proto->getDefinition(); + + // If this is a forward declaration and we are supposed to warn in this + // case, do it. + // FIXME: Recover nicely in the hidden case. + ObjCProtocolDecl *forwardDecl = nullptr; + if (warnOnIncompleteProtocols && + NestedProtocolHasNoDefinition(proto, forwardDecl)) { + Diag(identifierLocs[i], diag::warn_undef_protocolref) + << proto->getDeclName(); + Diag(forwardDecl->getLocation(), diag::note_protocol_decl_undefined) + << forwardDecl; + } + } + + DS.setProtocolQualifiers((Decl * const *)(protocols.data()), + protocols.size(), + const_cast<SourceLocation *>(identifierLocs.data()), + lAngleLoc); + if (rAngleLoc.isValid()) + DS.SetRangeEnd(rAngleLoc); + }; + + // Attempt to resolve all of the identifiers as protocols. + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { + ObjCProtocolDecl *proto = LookupProtocol(identifiers[i], identifierLocs[i]); + protocols.push_back(proto); + if (proto) + ++numProtocolsResolved; + } + + // If all of the names were protocols, these were protocol qualifiers. + if (numProtocolsResolved == identifiers.size()) + return resolvedAsProtocols(); + + // Attempt to resolve all of the identifiers as type names or + // Objective-C class names. The latter is technically ill-formed, + // but is probably something like \c NSArray<NSView *> missing the + // \c*. + typedef llvm::PointerUnion<TypeDecl *, ObjCInterfaceDecl *> TypeOrClassDecl; + SmallVector<TypeOrClassDecl, 4> typeDecls; + unsigned numTypeDeclsResolved = 0; + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { + NamedDecl *decl = LookupSingleName(S, identifiers[i], identifierLocs[i], + LookupOrdinaryName); + if (!decl) { + typeDecls.push_back(TypeOrClassDecl()); + continue; + } + + if (auto typeDecl = dyn_cast<TypeDecl>(decl)) { + typeDecls.push_back(typeDecl); + ++numTypeDeclsResolved; + continue; + } + + if (auto objcClass = dyn_cast<ObjCInterfaceDecl>(decl)) { + typeDecls.push_back(objcClass); + ++numTypeDeclsResolved; + continue; + } + + typeDecls.push_back(TypeOrClassDecl()); + } + + AttributeFactory attrFactory; + + // Local function that forms a reference to the given type or + // Objective-C class declaration. + auto resolveTypeReference = [&](TypeOrClassDecl typeDecl, SourceLocation loc) + -> TypeResult { + // Form declaration specifiers. They simply refer to the type. + DeclSpec DS(attrFactory); + const char* prevSpec; // unused + unsigned diagID; // unused + QualType type; + if (auto *actualTypeDecl = typeDecl.dyn_cast<TypeDecl *>()) + type = Context.getTypeDeclType(actualTypeDecl); + else + type = Context.getObjCInterfaceType(typeDecl.get<ObjCInterfaceDecl *>()); + TypeSourceInfo *parsedTSInfo = Context.getTrivialTypeSourceInfo(type, loc); + ParsedType parsedType = CreateParsedType(type, parsedTSInfo); + DS.SetTypeSpecType(DeclSpec::TST_typename, loc, prevSpec, diagID, + parsedType, Context.getPrintingPolicy()); + // Use the identifier location for the type source range. + DS.SetRangeStart(loc); + DS.SetRangeEnd(loc); + + // Form the declarator. + Declarator D(DS, Declarator::TypeNameContext); + + // If we have a typedef of an Objective-C class type that is missing a '*', + // add the '*'. + if (type->getAs<ObjCInterfaceType>()) { + SourceLocation starLoc = PP.getLocForEndOfToken(loc); + ParsedAttributes parsedAttrs(attrFactory); + D.AddTypeInfo(DeclaratorChunk::getPointer(/*typeQuals=*/0, starLoc, + SourceLocation(), + SourceLocation(), + SourceLocation(), + SourceLocation()), + parsedAttrs, + starLoc); + + // Diagnose the missing '*'. + Diag(loc, diag::err_objc_type_arg_missing_star) + << type + << FixItHint::CreateInsertion(starLoc, " *"); + } + + // Convert this to a type. + return ActOnTypeName(S, D); + }; + + // Local function that updates the declaration specifiers with + // type argument information. + auto resolvedAsTypeDecls = [&] { + assert(numTypeDeclsResolved == identifiers.size() && "Unresolved type decl"); + // Map type declarations to type arguments. + SmallVector<ParsedType, 4> typeArgs; + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { + // Map type reference to a type. + TypeResult type = resolveTypeReference(typeDecls[i], identifierLocs[i]); + if (!type.isUsable()) + return; + + typeArgs.push_back(type.get()); + } + + // Record the Objective-C type arguments. + DS.setObjCTypeArgs(lAngleLoc, typeArgs, rAngleLoc); + }; + + // If all of the identifiers can be resolved as type names or + // Objective-C class names, we have type arguments. + if (numTypeDeclsResolved == identifiers.size()) + return resolvedAsTypeDecls(); + + // Error recovery: some names weren't found, or we have a mix of + // type and protocol names. Go resolve all of the unresolved names + // and complain if we can't find a consistent answer. + LookupNameKind lookupKind = LookupAnyName; + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { + // If we already have a protocol or type. Check whether it is the + // right thing. + if (protocols[i] || typeDecls[i]) { + // If we haven't figured out whether we want types or protocols + // yet, try to figure it out from this name. + if (lookupKind == LookupAnyName) { + // If this name refers to both a protocol and a type (e.g., \c + // NSObject), don't conclude anything yet. + if (protocols[i] && typeDecls[i]) + continue; + + // Otherwise, let this name decide whether we'll be correcting + // toward types or protocols. + lookupKind = protocols[i] ? LookupObjCProtocolName + : LookupOrdinaryName; + continue; + } + + // If we want protocols and we have a protocol, there's nothing + // more to do. + if (lookupKind == LookupObjCProtocolName && protocols[i]) + continue; + + // If we want types and we have a type declaration, there's + // nothing more to do. + if (lookupKind == LookupOrdinaryName && typeDecls[i]) + continue; + + // We have a conflict: some names refer to protocols and others + // refer to types. + Diag(identifierLocs[i], diag::err_objc_type_args_and_protocols) + << (protocols[i] != nullptr) + << identifiers[i] + << identifiers[0] + << SourceRange(identifierLocs[0]); + + return; + } + + // Perform typo correction on the name. + TypoCorrection corrected = CorrectTypo( + DeclarationNameInfo(identifiers[i], identifierLocs[i]), lookupKind, S, + nullptr, + llvm::make_unique<ObjCTypeArgOrProtocolValidatorCCC>(Context, + lookupKind), + CTK_ErrorRecovery); + if (corrected) { + // Did we find a protocol? + if (auto proto = corrected.getCorrectionDeclAs<ObjCProtocolDecl>()) { + diagnoseTypo(corrected, + PDiag(diag::err_undeclared_protocol_suggest) + << identifiers[i]); + lookupKind = LookupObjCProtocolName; + protocols[i] = proto; + ++numProtocolsResolved; + continue; + } + + // Did we find a type? + if (auto typeDecl = corrected.getCorrectionDeclAs<TypeDecl>()) { + diagnoseTypo(corrected, + PDiag(diag::err_unknown_typename_suggest) + << identifiers[i]); + lookupKind = LookupOrdinaryName; + typeDecls[i] = typeDecl; + ++numTypeDeclsResolved; + continue; + } + + // Did we find an Objective-C class? + if (auto objcClass = corrected.getCorrectionDeclAs<ObjCInterfaceDecl>()) { + diagnoseTypo(corrected, + PDiag(diag::err_unknown_type_or_class_name_suggest) + << identifiers[i] << true); + lookupKind = LookupOrdinaryName; + typeDecls[i] = objcClass; + ++numTypeDeclsResolved; + continue; + } + } + + // We couldn't find anything. + Diag(identifierLocs[i], + (lookupKind == LookupAnyName ? diag::err_objc_type_arg_missing + : lookupKind == LookupObjCProtocolName ? diag::err_undeclared_protocol + : diag::err_unknown_typename)) + << identifiers[i]; + return; + } + + // If all of the names were (corrected to) protocols, these were + // protocol qualifiers. + if (numProtocolsResolved == identifiers.size()) + return resolvedAsProtocols(); + + // Otherwise, all of the names were (corrected to) types. + assert(numTypeDeclsResolved == identifiers.size() && "Not all types?"); + return resolvedAsTypeDecls(); +} + /// DiagnoseClassExtensionDupMethods - Check for duplicate declaration of /// a class method in its extension. /// @@ -1374,8 +1760,9 @@ Decl *Sema::ActOnStartClassImplementation( true); IDecl->startDefinition(); if (SDecl) { - IDecl->setSuperClass(SDecl); - IDecl->setSuperClassLoc(SuperClassLoc); + IDecl->setSuperClass(Context.getTrivialTypeSourceInfo( + Context.getObjCInterfaceType(SDecl), + SuperClassLoc)); IDecl->setEndOfDefinitionLoc(SuperClassLoc); } else { IDecl->setEndOfDefinitionLoc(ClassLoc); |