/// HasFeature - Return true if we recognize and implement the feature /// specified by the identifier as a standard language feature. static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { const LangOptions &LangOpts = PP.getLangOptions(); return llvm::StringSwitch<bool>(II->getName()) .Case("address_sanitizer", LangOpts.AddressSanitizer) .Case("attribute_analyzer_noreturn", true) .Case("attribute_availability", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) .Case("attribute_deprecated_with_message", true) .Case("attribute_ext_vector_type", true) .Case("attribute_ns_returns_not_retained", true) .Case("attribute_ns_returns_retained", true) .Case("attribute_ns_consumes_self", true) .Case("attribute_ns_consumed", true) .Case("attribute_cf_consumed", true) .Case("attribute_objc_ivar_unused", true) .Case("attribute_objc_method_family", true) .Case("attribute_overloadable", true) .Case("attribute_unavailable_with_message", true) .Case("blocks", LangOpts.Blocks) .Case("cxx_exceptions", LangOpts.Exceptions) .Case("cxx_rtti", LangOpts.RTTI) .Case("enumerator_attributes", true) // Objective-C features .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? .Case("objc_arc", LangOpts.ObjCAutoRefCount) .Case("objc_arc_weak", LangOpts.ObjCAutoRefCount && LangOpts.ObjCRuntimeHasWeak) .Case("objc_fixed_enum", LangOpts.ObjC2) .Case("objc_instancetype", LangOpts.ObjC2) .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) .Case("objc_weak_class", LangOpts.ObjCNonFragileABI) .Case("ownership_holds", true) .Case("ownership_returns", true) .Case("ownership_takes", true) .Case("arc_cf_code_audited", true) // C11 features .Case("c_alignas", LangOpts.C11) .Case("c_generic_selections", LangOpts.C11) .Case("c_static_assert", LangOpts.C11) // C++0x features .Case("cxx_access_control_sfinae", LangOpts.CPlusPlus0x) .Case("cxx_alias_templates", LangOpts.CPlusPlus0x) .Case("cxx_alignas", LangOpts.CPlusPlus0x) .Case("cxx_attributes", LangOpts.CPlusPlus0x) .Case("cxx_auto_type", LangOpts.CPlusPlus0x) //.Case("cxx_constexpr", false); .Case("cxx_decltype", LangOpts.CPlusPlus0x) .Case("cxx_default_function_template_args", LangOpts.CPlusPlus0x) .Case("cxx_defaulted_functions", LangOpts.CPlusPlus0x) .Case("cxx_delegating_constructors", LangOpts.CPlusPlus0x) .Case("cxx_deleted_functions", LangOpts.CPlusPlus0x) .Case("cxx_explicit_conversions", LangOpts.CPlusPlus0x) //.Case("cxx_generalized_initializers", LangOpts.CPlusPlus0x) .Case("cxx_implicit_moves", LangOpts.CPlusPlus0x) //.Case("cxx_inheriting_constructors", false) .Case("cxx_inline_namespaces", LangOpts.CPlusPlus0x) //.Case("cxx_lambdas", false) .Case("cxx_nonstatic_member_init", LangOpts.CPlusPlus0x) .Case("cxx_noexcept", LangOpts.CPlusPlus0x) .Case("cxx_nullptr", LangOpts.CPlusPlus0x) .Case("cxx_override_control", LangOpts.CPlusPlus0x) .Case("cxx_range_for", LangOpts.CPlusPlus0x) .Case("cxx_raw_string_literals", LangOpts.CPlusPlus0x) .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus0x) .Case("cxx_rvalue_references", LangOpts.CPlusPlus0x) .Case("cxx_strong_enums", LangOpts.CPlusPlus0x) .Case("cxx_static_assert", LangOpts.CPlusPlus0x) .Case("cxx_trailing_return", LangOpts.CPlusPlus0x) .Case("cxx_unicode_literals", LangOpts.CPlusPlus0x) //.Case("cxx_unrestricted_unions", false) //.Case("cxx_user_literals", false) .Case("cxx_variadic_templates", LangOpts.CPlusPlus0x) // Type traits .Case("has_nothrow_assign", LangOpts.CPlusPlus) .Case("has_nothrow_copy", LangOpts.CPlusPlus) .Case("has_nothrow_constructor", LangOpts.CPlusPlus) .Case("has_trivial_assign", LangOpts.CPlusPlus) .Case("has_trivial_copy", LangOpts.CPlusPlus) .Case("has_trivial_constructor", LangOpts.CPlusPlus) .Case("has_trivial_destructor", LangOpts.CPlusPlus) .Case("has_virtual_destructor", LangOpts.CPlusPlus) .Case("is_abstract", LangOpts.CPlusPlus) .Case("is_base_of", LangOpts.CPlusPlus) .Case("is_class", LangOpts.CPlusPlus) .Case("is_convertible_to", LangOpts.CPlusPlus) // __is_empty is available only if the horrible // "struct __is_empty" parsing hack hasn't been needed in this // translation unit. If it has, __is_empty reverts to a normal // identifier and __has_feature(is_empty) evaluates false. .Case("is_empty", LangOpts.CPlusPlus && PP.getIdentifierInfo("__is_empty")->getTokenID() != tok::identifier) .Case("is_enum", LangOpts.CPlusPlus) .Case("is_final", LangOpts.CPlusPlus) .Case("is_literal", LangOpts.CPlusPlus) .Case("is_standard_layout", LangOpts.CPlusPlus) // __is_pod is available only if the horrible // "struct __is_pod" parsing hack hasn't been needed in this // translation unit. If it has, __is_pod reverts to a normal // identifier and __has_feature(is_pod) evaluates false. .Case("is_pod", LangOpts.CPlusPlus && PP.getIdentifierInfo("__is_pod")->getTokenID() != tok::identifier) .Case("is_polymorphic", LangOpts.CPlusPlus) .Case("is_trivial", LangOpts.CPlusPlus) .Case("is_trivially_copyable", LangOpts.CPlusPlus) .Case("is_union", LangOpts.CPlusPlus) .Case("tls", PP.getTargetInfo().isTLSSupported()) .Case("underlying_type", LangOpts.CPlusPlus) .Default(false); }
void SynthesizeRemovalConsumer::removeSynthesizeDirectives(DeclContext *DC) { for(auto I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { // encounters @implementatian if (auto D = dyn_cast<ObjCImplDecl>(*I)) { // iterate through all the @synthesize / @dynamic directives for (auto PI = D->propimpl_begin(), PE = D->propimpl_end(); PI != PE; ++PI) { auto P = *PI; auto SR = P->getSourceRange(); // ignore if it's already an automatic @synthesize if (SR.getBegin().isInvalid()) { continue; } // ignore @dynamic if (P->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) { continue; } // if the ivar is not declared in the place where @synthesize is // (i.e. it is backed by some real ivar declared in @interface), // don't remove it auto PID = P->getPropertyIvarDecl(); if (P->getPropertyIvarDeclLoc() != PID->getLocation()) { continue; } // get the context in which the @property resides auto PD = P->getPropertyDecl(); auto PDC = PD->getDeclContext(); auto ID = dyn_cast<ObjCInterfaceDecl>(PDC); if (!ID) { continue; } // see if it's an @interface definition; this is false for @protocol // and we can only remove the synthesize decl for properties // declared within an @interface and not in @protocol if (!ID->isThisDeclarationADefinition()) { continue; } // check if the interface has the attribute // __attribute__((objc_requires_property_definitions)); if yes, the // directive cannot be removed bool attrObjCRequiresPropertyDefs = false; for (auto AI = ID->attr_begin(), AE = ID->attr_end(); AI != AE; ++AI) { if ((*AI)->getKind() == attr::ObjCRequiresPropertyDefs) { attrObjCRequiresPropertyDefs = true; break; } } if (attrObjCRequiresPropertyDefs) { continue; } // if there is both a manual getter *and* setter, it's equivalent to // a @dynamic, so we have to skip it auto GD = D->getInstanceMethod(PD->getGetterName()); auto SD = D->getInstanceMethod(PD->getSetterName()); if (GD && SD) { continue; } // another case for read-only properties if (GD && PD->isReadOnly()) { continue; } // scan past the ; token, get the end location SourceLocation EL = Lexer::findLocationAfterToken(SR.getEnd(), tok::semi, astContext->getSourceManager(), astContext->getLangOpts(), false); // probably run into a comma (compound directive) if (EL.isInvalid()) { continue; } // if it's a sub-directive after the comma, scan from the // beginning of the @synthesize token bool isCompoundDirective = false; const char *BCD = astContext->getSourceManager() .getCharacterData(SR.getBegin()); const char *ECD = astContext->getSourceManager() .getCharacterData(EL); while (BCD != ECD) { if (*BCD == ',') { isCompoundDirective = true; break; } ++BCD; } if (isCompoundDirective) { continue; } // check if the synthesized ivar name is not the same // as the property's name plus underscore std::string PIDN = PID->getNameAsString(); std::string PDN = PD->getNameAsString(); // handle the case where it's not @synthesize x = _x; std::string underscored = std::string("_") + PDN; if (PIDN != PDN) { if (PIDN != underscored) { continue; } } // see if the property is actually an ivar name of // some parent class! auto IF = preprocessor->getIdentifierInfo(underscored); if (IF) { auto IVDL = ID->lookupInstanceVariable(IF); if (IVDL && IVDL->getContainingInterface() != ID) { continue; } } // if parent class also have a property of the same name, // but of a different type, we can't remove it bool isOverridingProperty = false; auto S = ID->getSuperClass(); while (S) { auto SP = S->FindPropertyVisibleInPrimaryClass( PD->getIdentifier()); if (SP && (SP != PD)) { isOverridingProperty = true; break; } S = S->getSuperClass(); } if (isOverridingProperty) { continue; } // do the removal: record the removed directive removeSet.insert(P); // we want to remove the entire line if it becomes empty Rewriter::RewriteOptions RO; RO.RemoveLineIfEmpty = true; // and remove the @synthesize SourceRange RSR(SR.getBegin(), EL); rewriter.RemoveText(RSR, RO); } // PI } // D // descend into the next level (namespace, etc.) if (auto innerDC = dyn_cast<DeclContext>(*I)) { removeSynthesizeDirectives(innerDC); } } // DC }