Example #1
0
/// 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);
}
Example #2
0
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
}