Пример #1
0
  bool VisitVarDecl(const VarDecl *D) {
    // Bail out early if this location should not be checked.
    if (doIgnore(D->getLocation())) {
      return true;
    }

    const QualType qualType = D->getType();
    // Bail out if this type is either an enum or does not look like a real
    // value.
    if (qualType->isEnumeralType() || qualType->isBooleanType() ||
        qualType->isArithmeticType() == false) {
      return true;
    }

    const Type *t = qualType.getTypePtrOrNull();
    assert(t && "Type of arithmetic types has to be available.");
    const std::string typeName = qualType.getAsString();
    // If it is of the same type as "size_t" and does have "size_t" somewhere in
    // its name we can go with it.
    // Please note: This also allows a typedef for "unsigned long" to be named
    // e.g. "size_type" without any size indicator - which may or may not be a
    // good thing.
    if (context->hasSameUnqualifiedType(qualType, context->getSizeType()) &&
        typeName.find("size_t") != std::string::npos) {
      return true;
    }

    // char_t and wchar_t are not subject to this rule.
    const std::string needle = "char_t";
    if (std::equal(needle.rbegin(), needle.rend(), typeName.rbegin())) {
      return true;
    }

    const uint64_t typeSize = context->getTypeSize(t);
    const std::string sizeStr = llvm::utostr(typeSize);
    // For all remaining types, the number of occupied bits must be embedded in
    // the typename.
    if (typeName.rfind(sizeStr) == std::string::npos) {
      reportError(D->getLocation());
    }

    return true;
  }
Пример #2
0
static void SuggestInitializationFixit(Sema &S, const VarDecl *VD) {
  // Don't issue a fixit if there is already an initializer.
  if (VD->getInit())
    return;

  // Suggest possible initialization (if any).
  const char *initialization = 0;
  QualType VariableTy = VD->getType().getCanonicalType();

  if (VariableTy->isObjCObjectPointerType() ||
      VariableTy->isBlockPointerType()) {
    // Check if 'nil' is defined.
    if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil")))
      initialization = " = nil";
    else
      initialization = " = 0";
  }
  else if (VariableTy->isRealFloatingType())
    initialization = " = 0.0";
  else if (VariableTy->isBooleanType() && S.Context.getLangOptions().CPlusPlus)
    initialization = " = false";
  else if (VariableTy->isEnumeralType())
    return;
  else if (VariableTy->isPointerType() || VariableTy->isMemberPointerType()) {
    // Check if 'NULL' is defined.
    if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("NULL")))
      initialization = " = NULL";
    else
      initialization = " = 0";
  }
  else if (VariableTy->isScalarType())
    initialization = " = 0";

  if (initialization) {
    SourceLocation loc = S.PP.getLocForEndOfToken(VD->getLocEnd());
    S.Diag(loc, diag::note_var_fixit_add_initialization)
      << FixItHint::CreateInsertion(loc, initialization);
  }
}
/// \brief Build a new nested-name-specifier for "identifier::", as described
/// by ActOnCXXNestedNameSpecifier.
///
/// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
/// that it contains an extra parameter \p ScopeLookupResult, which provides
/// the result of name lookup within the scope of the nested-name-specifier
/// that was computed at template definition time.
///
/// If ErrorRecoveryLookup is true, then this call is used to improve error
/// recovery.  This means that it should not emit diagnostics, it should
/// just return true on failure.  It also means it should only return a valid
/// scope if it *knows* that the result is correct.  It should not return in a
/// dependent context, for example. Nor will it extend \p SS with the scope
/// specifier.
bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
                                       IdentifierInfo &Identifier,
                                       SourceLocation IdentifierLoc,
                                       SourceLocation CCLoc,
                                       QualType ObjectType,
                                       bool EnteringContext,
                                       CXXScopeSpec &SS,
                                       NamedDecl *ScopeLookupResult,
                                       bool ErrorRecoveryLookup) {
  LookupResult Found(*this, &Identifier, IdentifierLoc, 
                     LookupNestedNameSpecifierName);

  // Determine where to perform name lookup
  DeclContext *LookupCtx = 0;
  bool isDependent = false;
  if (!ObjectType.isNull()) {
    // This nested-name-specifier occurs in a member access expression, e.g.,
    // x->B::f, and we are looking into the type of the object.
    assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
    LookupCtx = computeDeclContext(ObjectType);
    isDependent = ObjectType->isDependentType();
  } else if (SS.isSet()) {
    // This nested-name-specifier occurs after another nested-name-specifier,
    // so look into the context associated with the prior nested-name-specifier.
    LookupCtx = computeDeclContext(SS, EnteringContext);
    isDependent = isDependentScopeSpecifier(SS);
    Found.setContextRange(SS.getRange());
  }


  bool ObjectTypeSearchedInScope = false;
  if (LookupCtx) {
    // Perform "qualified" name lookup into the declaration context we
    // computed, which is either the type of the base of a member access
    // expression or the declaration context associated with a prior
    // nested-name-specifier.

    // The declaration context must be complete.
    if (!LookupCtx->isDependentContext() &&
        RequireCompleteDeclContext(SS, LookupCtx))
      return true;

    LookupQualifiedName(Found, LookupCtx);

    if (!ObjectType.isNull() && Found.empty()) {
      // C++ [basic.lookup.classref]p4:
      //   If the id-expression in a class member access is a qualified-id of
      //   the form
      //
      //        class-name-or-namespace-name::...
      //
      //   the class-name-or-namespace-name following the . or -> operator is
      //   looked up both in the context of the entire postfix-expression and in
      //   the scope of the class of the object expression. If the name is found
      //   only in the scope of the class of the object expression, the name
      //   shall refer to a class-name. If the name is found only in the
      //   context of the entire postfix-expression, the name shall refer to a
      //   class-name or namespace-name. [...]
      //
      // Qualified name lookup into a class will not find a namespace-name,
      // so we do not need to diagnose that case specifically. However,
      // this qualified name lookup may find nothing. In that case, perform
      // unqualified name lookup in the given scope (if available) or
      // reconstruct the result from when name lookup was performed at template
      // definition time.
      if (S)
        LookupName(Found, S);
      else if (ScopeLookupResult)
        Found.addDecl(ScopeLookupResult);

      ObjectTypeSearchedInScope = true;
    }
  } else if (!isDependent) {
    // Perform unqualified name lookup in the current scope.
    LookupName(Found, S);
  }

  // If we performed lookup into a dependent context and did not find anything,
  // that's fine: just build a dependent nested-name-specifier.
  if (Found.empty() && isDependent &&
      !(LookupCtx && LookupCtx->isRecord() &&
        (!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
         !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
    // Don't speculate if we're just trying to improve error recovery.
    if (ErrorRecoveryLookup)
      return true;
    
    // We were not able to compute the declaration context for a dependent
    // base object type or prior nested-name-specifier, so this
    // nested-name-specifier refers to an unknown specialization. Just build
    // a dependent nested-name-specifier.
    SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc);
    return false;
  } 
  
  // FIXME: Deal with ambiguities cleanly.

  if (Found.empty() && !ErrorRecoveryLookup) {
    // We haven't found anything, and we're not recovering from a
    // different kind of error, so look for typos.
    DeclarationName Name = Found.getLookupName();
    TypoCorrection Corrected;
    Found.clear();
    if ((Corrected = CorrectTypo(Found.getLookupNameInfo(),
                                 Found.getLookupKind(), S, &SS, LookupCtx,
                                 EnteringContext, CTC_NoKeywords)) &&
        isAcceptableNestedNameSpecifier(Corrected.getCorrectionDecl())) {
      std::string CorrectedStr(Corrected.getAsString(getLangOptions()));
      std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions()));
      if (LookupCtx)
        Diag(Found.getNameLoc(), diag::err_no_member_suggest)
          << Name << LookupCtx << CorrectedQuotedStr << SS.getRange()
          << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr);
      else
        Diag(Found.getNameLoc(), diag::err_undeclared_var_use_suggest)
          << Name << CorrectedQuotedStr
          << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr);
      
      if (NamedDecl *ND = Corrected.getCorrectionDecl()) {
        Diag(ND->getLocation(), diag::note_previous_decl) << CorrectedQuotedStr;
        Found.addDecl(ND);
      }
      Found.setLookupName(Corrected.getCorrection());
    } else {
      Found.setLookupName(&Identifier);
    }
  }

  NamedDecl *SD = Found.getAsSingle<NamedDecl>();
  if (isAcceptableNestedNameSpecifier(SD)) {
    if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) {
      // C++ [basic.lookup.classref]p4:
      //   [...] If the name is found in both contexts, the
      //   class-name-or-namespace-name shall refer to the same entity.
      //
      // We already found the name in the scope of the object. Now, look
      // into the current scope (the scope of the postfix-expression) to
      // see if we can find the same name there. As above, if there is no
      // scope, reconstruct the result from the template instantiation itself.
      NamedDecl *OuterDecl;
      if (S) {
        LookupResult FoundOuter(*this, &Identifier, IdentifierLoc, 
                                LookupNestedNameSpecifierName);
        LookupName(FoundOuter, S);
        OuterDecl = FoundOuter.getAsSingle<NamedDecl>();
      } else
        OuterDecl = ScopeLookupResult;

      if (isAcceptableNestedNameSpecifier(OuterDecl) &&
          OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
          (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
           !Context.hasSameType(
                            Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
                               Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
         if (ErrorRecoveryLookup)
           return true;

         Diag(IdentifierLoc, 
              diag::err_nested_name_member_ref_lookup_ambiguous)
           << &Identifier;
         Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
           << ObjectType;
         Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);

         // Fall through so that we'll pick the name we found in the object
         // type, since that's probably what the user wanted anyway.
       }
    }

    // If we're just performing this lookup for error-recovery purposes, 
    // don't extend the nested-name-specifier. Just return now.
    if (ErrorRecoveryLookup)
      return false;
    
    if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
      SS.Extend(Context, Namespace, IdentifierLoc, CCLoc);
      return false;
    }

    if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) {
      SS.Extend(Context, Alias, IdentifierLoc, CCLoc);
      return false;
    }

    QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD));
    TypeLocBuilder TLB;
    if (isa<InjectedClassNameType>(T)) {
      InjectedClassNameTypeLoc InjectedTL
        = TLB.push<InjectedClassNameTypeLoc>(T);
      InjectedTL.setNameLoc(IdentifierLoc);
    } else if (isa<RecordType>(T)) {
      RecordTypeLoc RecordTL = TLB.push<RecordTypeLoc>(T);
      RecordTL.setNameLoc(IdentifierLoc);
    } else if (isa<TypedefType>(T)) {
      TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(T);
      TypedefTL.setNameLoc(IdentifierLoc);
    } else if (isa<EnumType>(T)) {
      EnumTypeLoc EnumTL = TLB.push<EnumTypeLoc>(T);
      EnumTL.setNameLoc(IdentifierLoc);
    } else if (isa<TemplateTypeParmType>(T)) {
      TemplateTypeParmTypeLoc TemplateTypeTL
        = TLB.push<TemplateTypeParmTypeLoc>(T);
      TemplateTypeTL.setNameLoc(IdentifierLoc);
    } else if (isa<UnresolvedUsingType>(T)) {
      UnresolvedUsingTypeLoc UnresolvedTL
        = TLB.push<UnresolvedUsingTypeLoc>(T);
      UnresolvedTL.setNameLoc(IdentifierLoc);
    } else if (isa<SubstTemplateTypeParmType>(T)) {
      SubstTemplateTypeParmTypeLoc TL 
        = TLB.push<SubstTemplateTypeParmTypeLoc>(T);
      TL.setNameLoc(IdentifierLoc);
    } else if (isa<SubstTemplateTypeParmPackType>(T)) {
      SubstTemplateTypeParmPackTypeLoc TL
        = TLB.push<SubstTemplateTypeParmPackTypeLoc>(T);
      TL.setNameLoc(IdentifierLoc);
    } else {
      llvm_unreachable("Unhandled TypeDecl node in nested-name-specifier");
    }

    if (T->isEnumeralType())
      Diag(IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec);

    SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T),
              CCLoc);
    return false;
  }

  // Otherwise, we have an error case.  If we don't want diagnostics, just
  // return an error now.
  if (ErrorRecoveryLookup)
    return true;

  // If we didn't find anything during our lookup, try again with
  // ordinary name lookup, which can help us produce better error
  // messages.
  if (Found.empty()) {
    Found.clear(LookupOrdinaryName);
    LookupName(Found, S);
  }

  // In Microsoft mode, if we are within a templated function and we can't
  // resolve Identifier, then extend the SS with Identifier. This will have 
  // the effect of resolving Identifier during template instantiation. 
  // The goal is to be able to resolve a function call whose
  // nested-name-specifier is located inside a dependent base class.
  // Example: 
  //
  // class C {
  // public:
  //    static void foo2() {  }
  // };
  // template <class T> class A { public: typedef C D; };
  //
  // template <class T> class B : public A<T> {
  // public:
  //   void foo() { D::foo2(); }
  // };
  if (getLangOptions().MicrosoftExt) {
    DeclContext *DC = LookupCtx ? LookupCtx : CurContext;
    if (DC->isDependentContext() && DC->isFunctionOrMethod()) {
      SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc);
      return false;
    }
  }

  unsigned DiagID;
  if (!Found.empty())
    DiagID = diag::err_expected_class_or_namespace;
  else if (SS.isSet()) {
    Diag(IdentifierLoc, diag::err_no_member) 
      << &Identifier << LookupCtx << SS.getRange();
    return true;
  } else
    DiagID = diag::err_undeclared_var_use;

  if (SS.isSet())
    Diag(IdentifierLoc, DiagID) << &Identifier << SS.getRange();
  else
    Diag(IdentifierLoc, DiagID) << &Identifier;

  return true;
}
Пример #4
0
/// Build a new nested-name-specifier for "identifier::", as described
/// by ActOnCXXNestedNameSpecifier.
///
/// \param S Scope in which the nested-name-specifier occurs.
/// \param IdInfo Parser information about an identifier in the
///        nested-name-spec.
/// \param EnteringContext If true, enter the context specified by the
///        nested-name-specifier.
/// \param SS Optional nested name specifier preceding the identifier.
/// \param ScopeLookupResult Provides the result of name lookup within the
///        scope of the nested-name-specifier that was computed at template
///        definition time.
/// \param ErrorRecoveryLookup Specifies if the method is called to improve
///        error recovery and what kind of recovery is performed.
/// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':'
///        are allowed.  The bool value pointed by this parameter is set to
///       'true' if the identifier is treated as if it was followed by ':',
///        not '::'.
/// \param OnlyNamespace If true, only considers namespaces in lookup.
///
/// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
/// that it contains an extra parameter \p ScopeLookupResult, which provides
/// the result of name lookup within the scope of the nested-name-specifier
/// that was computed at template definition time.
///
/// If ErrorRecoveryLookup is true, then this call is used to improve error
/// recovery.  This means that it should not emit diagnostics, it should
/// just return true on failure.  It also means it should only return a valid
/// scope if it *knows* that the result is correct.  It should not return in a
/// dependent context, for example. Nor will it extend \p SS with the scope
/// specifier.
bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
                                       bool EnteringContext, CXXScopeSpec &SS,
                                       NamedDecl *ScopeLookupResult,
                                       bool ErrorRecoveryLookup,
                                       bool *IsCorrectedToColon,
                                       bool OnlyNamespace) {
  if (IdInfo.Identifier->isEditorPlaceholder())
    return true;
  LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
                     OnlyNamespace ? LookupNamespaceName
                                   : LookupNestedNameSpecifierName);
  QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);

  // Determine where to perform name lookup
  DeclContext *LookupCtx = nullptr;
  bool isDependent = false;
  if (IsCorrectedToColon)
    *IsCorrectedToColon = false;
  if (!ObjectType.isNull()) {
    // This nested-name-specifier occurs in a member access expression, e.g.,
    // x->B::f, and we are looking into the type of the object.
    assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
    LookupCtx = computeDeclContext(ObjectType);
    isDependent = ObjectType->isDependentType();
  } else if (SS.isSet()) {
    // This nested-name-specifier occurs after another nested-name-specifier,
    // so look into the context associated with the prior nested-name-specifier.
    LookupCtx = computeDeclContext(SS, EnteringContext);
    isDependent = isDependentScopeSpecifier(SS);
    Found.setContextRange(SS.getRange());
  }

  bool ObjectTypeSearchedInScope = false;
  if (LookupCtx) {
    // Perform "qualified" name lookup into the declaration context we
    // computed, which is either the type of the base of a member access
    // expression or the declaration context associated with a prior
    // nested-name-specifier.

    // The declaration context must be complete.
    if (!LookupCtx->isDependentContext() &&
        RequireCompleteDeclContext(SS, LookupCtx))
      return true;

    LookupQualifiedName(Found, LookupCtx);

    if (!ObjectType.isNull() && Found.empty()) {
      // C++ [basic.lookup.classref]p4:
      //   If the id-expression in a class member access is a qualified-id of
      //   the form
      //
      //        class-name-or-namespace-name::...
      //
      //   the class-name-or-namespace-name following the . or -> operator is
      //   looked up both in the context of the entire postfix-expression and in
      //   the scope of the class of the object expression. If the name is found
      //   only in the scope of the class of the object expression, the name
      //   shall refer to a class-name. If the name is found only in the
      //   context of the entire postfix-expression, the name shall refer to a
      //   class-name or namespace-name. [...]
      //
      // Qualified name lookup into a class will not find a namespace-name,
      // so we do not need to diagnose that case specifically. However,
      // this qualified name lookup may find nothing. In that case, perform
      // unqualified name lookup in the given scope (if available) or
      // reconstruct the result from when name lookup was performed at template
      // definition time.
      if (S)
        LookupName(Found, S);
      else if (ScopeLookupResult)
        Found.addDecl(ScopeLookupResult);

      ObjectTypeSearchedInScope = true;
    }
  } else if (!isDependent) {
    // Perform unqualified name lookup in the current scope.
    LookupName(Found, S);
  }

  if (Found.isAmbiguous())
    return true;

  // If we performed lookup into a dependent context and did not find anything,
  // that's fine: just build a dependent nested-name-specifier.
  if (Found.empty() && isDependent &&
      !(LookupCtx && LookupCtx->isRecord() &&
        (!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
         !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
    // Don't speculate if we're just trying to improve error recovery.
    if (ErrorRecoveryLookup)
      return true;

    // We were not able to compute the declaration context for a dependent
    // base object type or prior nested-name-specifier, so this
    // nested-name-specifier refers to an unknown specialization. Just build
    // a dependent nested-name-specifier.
    SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
    return false;
  }

  if (Found.empty() && !ErrorRecoveryLookup) {
    // If identifier is not found as class-name-or-namespace-name, but is found
    // as other entity, don't look for typos.
    LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName);
    if (LookupCtx)
      LookupQualifiedName(R, LookupCtx);
    else if (S && !isDependent)
      LookupName(R, S);
    if (!R.empty()) {
      // Don't diagnose problems with this speculative lookup.
      R.suppressDiagnostics();
      // The identifier is found in ordinary lookup. If correction to colon is
      // allowed, suggest replacement to ':'.
      if (IsCorrectedToColon) {
        *IsCorrectedToColon = true;
        Diag(IdInfo.CCLoc, diag::err_nested_name_spec_is_not_class)
            << IdInfo.Identifier << getLangOpts().CPlusPlus
            << FixItHint::CreateReplacement(IdInfo.CCLoc, ":");
        if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
          Diag(ND->getLocation(), diag::note_declared_at);
        return true;
      }
      // Replacement '::' -> ':' is not allowed, just issue respective error.
      Diag(R.getNameLoc(), OnlyNamespace
                               ? unsigned(diag::err_expected_namespace_name)
                               : unsigned(diag::err_expected_class_or_namespace))
          << IdInfo.Identifier << getLangOpts().CPlusPlus;
      if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
        Diag(ND->getLocation(), diag::note_entity_declared_at)
            << IdInfo.Identifier;
      return true;
    }
  }

  if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) {
    // We haven't found anything, and we're not recovering from a
    // different kind of error, so look for typos.
    DeclarationName Name = Found.getLookupName();
    Found.clear();
    NestedNameSpecifierValidatorCCC CCC(*this);
    if (TypoCorrection Corrected = CorrectTypo(
            Found.getLookupNameInfo(), Found.getLookupKind(), S, &SS, CCC,
            CTK_ErrorRecovery, LookupCtx, EnteringContext)) {
      if (LookupCtx) {
        bool DroppedSpecifier =
            Corrected.WillReplaceSpecifier() &&
            Name.getAsString() == Corrected.getAsString(getLangOpts());
        if (DroppedSpecifier)
          SS.clear();
        diagnoseTypo(Corrected, PDiag(diag::err_no_member_suggest)
                                  << Name << LookupCtx << DroppedSpecifier
                                  << SS.getRange());
      } else
        diagnoseTypo(Corrected, PDiag(diag::err_undeclared_var_use_suggest)
                                  << Name);

      if (Corrected.getCorrectionSpecifier())
        SS.MakeTrivial(Context, Corrected.getCorrectionSpecifier(),
                       SourceRange(Found.getNameLoc()));

      if (NamedDecl *ND = Corrected.getFoundDecl())
        Found.addDecl(ND);
      Found.setLookupName(Corrected.getCorrection());
    } else {
      Found.setLookupName(IdInfo.Identifier);
    }
  }

  NamedDecl *SD =
      Found.isSingleResult() ? Found.getRepresentativeDecl() : nullptr;
  bool IsExtension = false;
  bool AcceptSpec = isAcceptableNestedNameSpecifier(SD, &IsExtension);
  if (!AcceptSpec && IsExtension) {
    AcceptSpec = true;
    Diag(IdInfo.IdentifierLoc, diag::ext_nested_name_spec_is_enum);
  }
  if (AcceptSpec) {
    if (!ObjectType.isNull() && !ObjectTypeSearchedInScope &&
        !getLangOpts().CPlusPlus11) {
      // C++03 [basic.lookup.classref]p4:
      //   [...] If the name is found in both contexts, the
      //   class-name-or-namespace-name shall refer to the same entity.
      //
      // We already found the name in the scope of the object. Now, look
      // into the current scope (the scope of the postfix-expression) to
      // see if we can find the same name there. As above, if there is no
      // scope, reconstruct the result from the template instantiation itself.
      //
      // Note that C++11 does *not* perform this redundant lookup.
      NamedDecl *OuterDecl;
      if (S) {
        LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
                                LookupNestedNameSpecifierName);
        LookupName(FoundOuter, S);
        OuterDecl = FoundOuter.getAsSingle<NamedDecl>();
      } else
        OuterDecl = ScopeLookupResult;

      if (isAcceptableNestedNameSpecifier(OuterDecl) &&
          OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
          (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
           !Context.hasSameType(
                            Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
                               Context.getTypeDeclType(cast<TypeDecl>(SD))))) {
        if (ErrorRecoveryLookup)
          return true;

         Diag(IdInfo.IdentifierLoc,
              diag::err_nested_name_member_ref_lookup_ambiguous)
           << IdInfo.Identifier;
         Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
           << ObjectType;
         Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);

         // Fall through so that we'll pick the name we found in the object
         // type, since that's probably what the user wanted anyway.
       }
    }

    if (auto *TD = dyn_cast_or_null<TypedefNameDecl>(SD))
      MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);

    // If we're just performing this lookup for error-recovery purposes,
    // don't extend the nested-name-specifier. Just return now.
    if (ErrorRecoveryLookup)
      return false;

    // The use of a nested name specifier may trigger deprecation warnings.
    DiagnoseUseOfDecl(SD, IdInfo.CCLoc);

    if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) {
      SS.Extend(Context, Namespace, IdInfo.IdentifierLoc, IdInfo.CCLoc);
      return false;
    }

    if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) {
      SS.Extend(Context, Alias, IdInfo.IdentifierLoc, IdInfo.CCLoc);
      return false;
    }

    QualType T =
        Context.getTypeDeclType(cast<TypeDecl>(SD->getUnderlyingDecl()));
    TypeLocBuilder TLB;
    if (isa<InjectedClassNameType>(T)) {
      InjectedClassNameTypeLoc InjectedTL
        = TLB.push<InjectedClassNameTypeLoc>(T);
      InjectedTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<RecordType>(T)) {
      RecordTypeLoc RecordTL = TLB.push<RecordTypeLoc>(T);
      RecordTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<TypedefType>(T)) {
      TypedefTypeLoc TypedefTL = TLB.push<TypedefTypeLoc>(T);
      TypedefTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<EnumType>(T)) {
      EnumTypeLoc EnumTL = TLB.push<EnumTypeLoc>(T);
      EnumTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<TemplateTypeParmType>(T)) {
      TemplateTypeParmTypeLoc TemplateTypeTL
        = TLB.push<TemplateTypeParmTypeLoc>(T);
      TemplateTypeTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<UnresolvedUsingType>(T)) {
      UnresolvedUsingTypeLoc UnresolvedTL
        = TLB.push<UnresolvedUsingTypeLoc>(T);
      UnresolvedTL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<SubstTemplateTypeParmType>(T)) {
      SubstTemplateTypeParmTypeLoc TL
        = TLB.push<SubstTemplateTypeParmTypeLoc>(T);
      TL.setNameLoc(IdInfo.IdentifierLoc);
    } else if (isa<SubstTemplateTypeParmPackType>(T)) {
      SubstTemplateTypeParmPackTypeLoc TL
        = TLB.push<SubstTemplateTypeParmPackTypeLoc>(T);
      TL.setNameLoc(IdInfo.IdentifierLoc);
    } else {
      llvm_unreachable("Unhandled TypeDecl node in nested-name-specifier");
    }

    if (T->isEnumeralType())
      Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec);

    SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T),
              IdInfo.CCLoc);
    return false;
  }

  // Otherwise, we have an error case.  If we don't want diagnostics, just
  // return an error now.
  if (ErrorRecoveryLookup)
    return true;

  // If we didn't find anything during our lookup, try again with
  // ordinary name lookup, which can help us produce better error
  // messages.
  if (Found.empty()) {
    Found.clear(LookupOrdinaryName);
    LookupName(Found, S);
  }

  // In Microsoft mode, if we are within a templated function and we can't
  // resolve Identifier, then extend the SS with Identifier. This will have
  // the effect of resolving Identifier during template instantiation.
  // The goal is to be able to resolve a function call whose
  // nested-name-specifier is located inside a dependent base class.
  // Example:
  //
  // class C {
  // public:
  //    static void foo2() {  }
  // };
  // template <class T> class A { public: typedef C D; };
  //
  // template <class T> class B : public A<T> {
  // public:
  //   void foo() { D::foo2(); }
  // };
  if (getLangOpts().MSVCCompat) {
    DeclContext *DC = LookupCtx ? LookupCtx : CurContext;
    if (DC->isDependentContext() && DC->isFunctionOrMethod()) {
      CXXRecordDecl *ContainingClass = dyn_cast<CXXRecordDecl>(DC->getParent());
      if (ContainingClass && ContainingClass->hasAnyDependentBases()) {
        Diag(IdInfo.IdentifierLoc,
             diag::ext_undeclared_unqual_id_with_dependent_base)
            << IdInfo.Identifier << ContainingClass;
        SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
                  IdInfo.CCLoc);
        return false;
      }
    }
  }

  if (!Found.empty()) {
    if (TypeDecl *TD = Found.getAsSingle<TypeDecl>())
      Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace)
          << Context.getTypeDeclType(TD) << getLangOpts().CPlusPlus;
    else {
      Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace)
          << IdInfo.Identifier << getLangOpts().CPlusPlus;
      if (NamedDecl *ND = Found.getAsSingle<NamedDecl>())
        Diag(ND->getLocation(), diag::note_entity_declared_at)
            << IdInfo.Identifier;
    }
  } else if (SS.isSet())
    Diag(IdInfo.IdentifierLoc, diag::err_no_member) << IdInfo.Identifier
        << LookupCtx << SS.getRange();
  else
    Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use)
        << IdInfo.Identifier;

  return true;
}
Пример #5
0
/// ActOnCXXNew - Parsed a C++ 'new' expression (C++ 5.3.4), as in e.g.:
/// @code new (memory) int[size][4] @endcode
/// or
/// @code ::new Foo(23, "hello") @endcode
/// For the interpretation of this heap of arguments, consult the base version.
Action::OwningExprResult
Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
                  SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
                  SourceLocation PlacementRParen, bool ParenTypeId,
                  Declarator &D, SourceLocation ConstructorLParen,
                  MultiExprArg ConstructorArgs,
                  SourceLocation ConstructorRParen)
{
  Expr *ArraySize = 0;
  unsigned Skip = 0;
  // If the specified type is an array, unwrap it and save the expression.
  if (D.getNumTypeObjects() > 0 &&
      D.getTypeObject(0).Kind == DeclaratorChunk::Array) {
    DeclaratorChunk &Chunk = D.getTypeObject(0);
    if (Chunk.Arr.hasStatic)
      return ExprError(Diag(Chunk.Loc, diag::err_static_illegal_in_new)
        << D.getSourceRange());
    if (!Chunk.Arr.NumElts)
      return ExprError(Diag(Chunk.Loc, diag::err_array_new_needs_size)
        << D.getSourceRange());
    ArraySize = static_cast<Expr*>(Chunk.Arr.NumElts);
    Skip = 1;
  }

  QualType AllocType = GetTypeForDeclarator(D, /*Scope=*/0, Skip);
  if (D.getInvalidType())
    return ExprError();

  if (CheckAllocatedType(AllocType, D))
    return ExprError();

  QualType ResultType = AllocType->isDependentType()
                          ? Context.DependentTy
                          : Context.getPointerType(AllocType);

  // That every array dimension except the first is constant was already
  // checked by the type check above.

  // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral
  //   or enumeration type with a non-negative value."
  if (ArraySize && !ArraySize->isTypeDependent()) {
    QualType SizeType = ArraySize->getType();
    if (!SizeType->isIntegralType() && !SizeType->isEnumeralType())
      return ExprError(Diag(ArraySize->getSourceRange().getBegin(),
                            diag::err_array_size_not_integral)
        << SizeType << ArraySize->getSourceRange());
    // Let's see if this is a constant < 0. If so, we reject it out of hand.
    // We don't care about special rules, so we tell the machinery it's not
    // evaluated - it gives us a result in more cases.
    if (!ArraySize->isValueDependent()) {
      llvm::APSInt Value;
      if (ArraySize->isIntegerConstantExpr(Value, Context, 0, false)) {
        if (Value < llvm::APSInt(
                        llvm::APInt::getNullValue(Value.getBitWidth()), false))
          return ExprError(Diag(ArraySize->getSourceRange().getBegin(),
                           diag::err_typecheck_negative_array_size)
            << ArraySize->getSourceRange());
      }
    }
  }

  FunctionDecl *OperatorNew = 0;
  FunctionDecl *OperatorDelete = 0;
  Expr **PlaceArgs = (Expr**)PlacementArgs.get();
  unsigned NumPlaceArgs = PlacementArgs.size();
  if (!AllocType->isDependentType() &&
      !Expr::hasAnyTypeDependentArguments(PlaceArgs, NumPlaceArgs) &&
      FindAllocationFunctions(StartLoc,
                              SourceRange(PlacementLParen, PlacementRParen),
                              UseGlobal, AllocType, ArraySize, PlaceArgs,
                              NumPlaceArgs, OperatorNew, OperatorDelete))
    return ExprError();

  bool Init = ConstructorLParen.isValid();
  // --- Choosing a constructor ---
  // C++ 5.3.4p15
  // 1) If T is a POD and there's no initializer (ConstructorLParen is invalid)
  //   the object is not initialized. If the object, or any part of it, is
  //   const-qualified, it's an error.
  // 2) If T is a POD and there's an empty initializer, the object is value-
  //   initialized.
  // 3) If T is a POD and there's one initializer argument, the object is copy-
  //   constructed.
  // 4) If T is a POD and there's more initializer arguments, it's an error.
  // 5) If T is not a POD, the initializer arguments are used as constructor
  //   arguments.
  //
  // Or by the C++0x formulation:
  // 1) If there's no initializer, the object is default-initialized according
  //    to C++0x rules.
  // 2) Otherwise, the object is direct-initialized.
  CXXConstructorDecl *Constructor = 0;
  Expr **ConsArgs = (Expr**)ConstructorArgs.get();
  unsigned NumConsArgs = ConstructorArgs.size();
  if (AllocType->isDependentType()) {
    // Skip all the checks.
  }
  // FIXME: Should check for primitive/aggregate here, not record.
  else if (const RecordType *RT = AllocType->getAsRecordType()) {
    // FIXME: This is incorrect for when there is an empty initializer and
    // no user-defined constructor. Must zero-initialize, not default-construct.
    Constructor = PerformInitializationByConstructor(
                      AllocType, ConsArgs, NumConsArgs,
                      D.getSourceRange().getBegin(),
                      SourceRange(D.getSourceRange().getBegin(),
                                  ConstructorRParen),
                      RT->getDecl()->getDeclName(),
                      NumConsArgs != 0 ? IK_Direct : IK_Default);
    if (!Constructor)
      return ExprError();
  } else {
    if (!Init) {
      // FIXME: Check that no subpart is const.
      if (AllocType.isConstQualified())
        return ExprError(Diag(StartLoc, diag::err_new_uninitialized_const)
          << D.getSourceRange());
    } else if (NumConsArgs == 0) {
      // Object is value-initialized. Do nothing.
    } else if (NumConsArgs == 1) {
      // Object is direct-initialized.
      // FIXME: WHAT DeclarationName do we pass in here?
      if (CheckInitializerTypes(ConsArgs[0], AllocType, StartLoc,
                                DeclarationName() /*AllocType.getAsString()*/,
                                /*DirectInit=*/true))
        return ExprError();
    } else {
      return ExprError(Diag(StartLoc,
                            diag::err_builtin_direct_init_more_than_one_arg)
        << SourceRange(ConstructorLParen, ConstructorRParen));
    }
  }

  // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16)

  PlacementArgs.release();
  ConstructorArgs.release();
  return Owned(new (Context) CXXNewExpr(UseGlobal, OperatorNew, PlaceArgs,
                        NumPlaceArgs, ParenTypeId, ArraySize, Constructor, Init,
                        ConsArgs, NumConsArgs, OperatorDelete, ResultType,
                        StartLoc, Init ? ConstructorRParen : SourceLocation()));
}
Пример #6
0
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");

  auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
                                                   CastExpr->getRParenLoc());
  // Ignore casts in macros.
  if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
    return;

  // Casting to void is an idiomatic way to mute "unused variable" and similar
  // warnings.
  if (CastExpr->getTypeAsWritten()->isVoidType())
    return;

  QualType SourceType =
      CastExpr->getSubExprAsWritten()->getType().getCanonicalType();
  QualType DestType = CastExpr->getTypeAsWritten().getCanonicalType();

  if (SourceType == DestType) {
    diag(CastExpr->getLocStart(), "Redundant cast to the same type.")
        << FixItHint::CreateRemoval(ParenRange);
    return;
  }

  // The rest of this check is only relevant to C++.
  if (!Result.Context->getLangOpts().CPlusPlus)
    return;

  // Leave type spelling exactly as it was (unlike
  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
  StringRef DestTypeString = Lexer::getSourceText(
      CharSourceRange::getTokenRange(
          CastExpr->getLParenLoc().getLocWithOffset(1),
          CastExpr->getRParenLoc().getLocWithOffset(-1)),
      *Result.SourceManager, Result.Context->getLangOpts());

  auto diag_builder =
      diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0");

  auto ReplaceWithCast = [&](StringRef CastType) {
    diag_builder << ("Use " + CastType + ".").str();

    const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
    std::string CastText = (CastType + "<" + DestTypeString + ">").str();
    if (!isa<ParenExpr>(SubExpr)) {
      CastText.push_back('(');
      diag_builder << FixItHint::CreateInsertion(
          Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0,
                                     *Result.SourceManager,
                                     Result.Context->getLangOpts()),
          ")");
    }
    diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
  };
  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
  switch (CastExpr->getCastKind()) {
  case CK_NoOp:
    if (needsConstCast(SourceType, DestType) &&
        pointedTypesAreEqual(SourceType, DestType)) {
      ReplaceWithCast("const_cast");
      return;
    }
    if (DestType->isReferenceType() &&
        (SourceType.getNonReferenceType() ==
             DestType.getNonReferenceType().withConst() ||
         SourceType.getNonReferenceType() == DestType.getNonReferenceType())) {
      ReplaceWithCast("const_cast");
      return;
    }
    // FALLTHROUGH
  case clang::CK_IntegralCast:
    // Convert integral and no-op casts between builtin types and enums to
    // static_cast. A cast from enum to integer may be unnecessary, but it's
    // still retained.
    if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
        (DestType->isBuiltinType() || DestType->isEnumeralType())) {
      ReplaceWithCast("static_cast");
      return;
    }
    break;
  case CK_BitCast:
    // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
    if (!needsConstCast(SourceType, DestType)) {
      ReplaceWithCast("reinterpret_cast");
      return;
    }
    break;
  default:
    break;
  }

  diag_builder << "Use static_cast/const_cast/reinterpret_cast.";
}
Пример #7
0
/// CheckStaticCast - Check that a static_cast\<DestType\>(SrcExpr) is valid.
/// Refer to C++ 5.2.9 for details. Static casts are mostly used for making
/// implicit conversions explicit and getting rid of data loss warnings.
void
CheckStaticCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
                const SourceRange &OpRange)
{
  // The order the tests is not entirely arbitrary. There is one conversion
  // that can be handled in two different ways. Given:
  // struct A {};
  // struct B : public A {
  //   B(); B(const A&);
  // };
  // const A &a = B();
  // the cast static_cast<const B&>(a) could be seen as either a static
  // reference downcast, or an explicit invocation of the user-defined
  // conversion using B's conversion constructor.
  // DR 427 specifies that the downcast is to be applied here.

  // FIXME: With N2812, casts to rvalue refs will change.

  // C++ 5.2.9p4: Any expression can be explicitly converted to type "cv void".
  if (DestType->isVoidType()) {
    return;
  }

  // C++ 5.2.9p5, reference downcast.
  // See the function for details.
  // DR 427 specifies that this is to be applied before paragraph 2.
  if (TryStaticReferenceDowncast(Self, SrcExpr, DestType, OpRange)
      > TSC_NotApplicable) {
    return;
  }

  // N2844 5.2.9p3: An lvalue of type "cv1 T1" can be cast to type "rvalue
  //   reference to cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
  if (TryLValueToRValueCast(Self, SrcExpr, DestType, OpRange) >
      TSC_NotApplicable) {
    return;
  }

  // C++ 5.2.9p2: An expression e can be explicitly converted to a type T
  //   [...] if the declaration "T t(e);" is well-formed, [...].
  if (TryStaticImplicitCast(Self, SrcExpr, DestType, OpRange) >
      TSC_NotApplicable) {
    return;
  }

  // C++ 5.2.9p6: May apply the reverse of any standard conversion, except
  // lvalue-to-rvalue, array-to-pointer, function-to-pointer, and boolean
  // conversions, subject to further restrictions.
  // Also, C++ 5.2.9p1 forbids casting away constness, which makes reversal
  // of qualification conversions impossible.

  // The lvalue-to-rvalue, array-to-pointer and function-to-pointer conversions
  // are applied to the expression.
  QualType OrigSrcType = SrcExpr->getType();
  Self.DefaultFunctionArrayConversion(SrcExpr);

  QualType SrcType = Self.Context.getCanonicalType(SrcExpr->getType());

  // Reverse integral promotion/conversion. All such conversions are themselves
  // again integral promotions or conversions and are thus already handled by
  // p2 (TryDirectInitialization above).
  // (Note: any data loss warnings should be suppressed.)
  // The exception is the reverse of enum->integer, i.e. integer->enum (and
  // enum->enum). See also C++ 5.2.9p7.
  // The same goes for reverse floating point promotion/conversion and
  // floating-integral conversions. Again, only floating->enum is relevant.
  if (DestType->isEnumeralType()) {
    if (SrcType->isComplexType() || SrcType->isVectorType()) {
      // Fall through - these cannot be converted.
    } else if (SrcType->isArithmeticType() || SrcType->isEnumeralType()) {
      return;
    }
  }

  // Reverse pointer upcast. C++ 4.10p3 specifies pointer upcast.
  // C++ 5.2.9p8 additionally disallows a cast path through virtual inheritance.
  if (TryStaticPointerDowncast(Self, SrcType, DestType, OpRange)
      > TSC_NotApplicable) {
    return;
  }

  // Reverse member pointer conversion. C++ 4.11 specifies member pointer
  // conversion. C++ 5.2.9p9 has additional information.
  // DR54's access restrictions apply here also.
  if (TryStaticMemberPointerUpcast(Self, SrcType, DestType, OpRange)
      > TSC_NotApplicable) {
    return;
  }

  // Reverse pointer conversion to void*. C++ 4.10.p2 specifies conversion to
  // void*. C++ 5.2.9p10 specifies additional restrictions, which really is
  // just the usual constness stuff.
  if (const PointerType *SrcPointer = SrcType->getAsPointerType()) {
    QualType SrcPointee = SrcPointer->getPointeeType();
    if (SrcPointee->isVoidType()) {
      if (const PointerType *DestPointer = DestType->getAsPointerType()) {
        QualType DestPointee = DestPointer->getPointeeType();
        if (DestPointee->isIncompleteOrObjectType()) {
          // This is definitely the intended conversion, but it might fail due
          // to a const violation.
          if (!DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
            Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
              << "static_cast" << DestType << OrigSrcType << OpRange;
          }
          return;
        }
      }
    }
  }

  // We tried everything. Everything! Nothing works! :-(
  // FIXME: Error reporting could be a lot better. Should store the reason why
  // every substep failed and, at the end, select the most specific and report
  // that.
  Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
    << "static_cast" << DestType << OrigSrcType
    << OpRange;
}
Пример #8
0
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
/// valid.
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
/// like this:
/// char *bytes = reinterpret_cast\<char*\>(int_ptr);
void
CheckReinterpretCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
                     const SourceRange &OpRange, const SourceRange &DestRange)
{
  QualType OrigDestType = DestType, OrigSrcType = SrcExpr->getType();

  DestType = Self.Context.getCanonicalType(DestType);
  QualType SrcType = SrcExpr->getType();
  if (const LValueReferenceType *DestTypeTmp =
        DestType->getAsLValueReferenceType()) {
    if (SrcExpr->isLvalue(Self.Context) != Expr::LV_Valid) {
      // Cannot cast non-lvalue to reference type.
      Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_rvalue)
        << "reinterpret_cast" << OrigDestType << SrcExpr->getSourceRange();
      return;
    }

    // C++ 5.2.10p10: [...] a reference cast reinterpret_cast<T&>(x) has the
    //   same effect as the conversion *reinterpret_cast<T*>(&x) with the
    //   built-in & and * operators.
    // This code does this transformation for the checked types.
    DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
    SrcType = Self.Context.getPointerType(SrcType);
  } else if (const RValueReferenceType *DestTypeTmp =
               DestType->getAsRValueReferenceType()) {
    // Both the reference conversion and the rvalue rules apply.
    Self.DefaultFunctionArrayConversion(SrcExpr);
    SrcType = SrcExpr->getType();

    DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
    SrcType = Self.Context.getPointerType(SrcType);
  } else {
    // C++ 5.2.10p1: [...] the lvalue-to-rvalue, array-to-pointer, and
    //   function-to-pointer standard conversions are performed on the
    //   expression v.
    Self.DefaultFunctionArrayConversion(SrcExpr);
    SrcType = SrcExpr->getType();
  }

  // Canonicalize source for comparison.
  SrcType = Self.Context.getCanonicalType(SrcType);

  const MemberPointerType *DestMemPtr = DestType->getAsMemberPointerType(),
                          *SrcMemPtr = SrcType->getAsMemberPointerType();
  if (DestMemPtr && SrcMemPtr) {
    // C++ 5.2.10p9: An rvalue of type "pointer to member of X of type T1"
    //   can be explicitly converted to an rvalue of type "pointer to member
    //   of Y of type T2" if T1 and T2 are both function types or both object
    //   types.
    if (DestMemPtr->getPointeeType()->isFunctionType() !=
        SrcMemPtr->getPointeeType()->isFunctionType()) {
      Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
        << "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
      return;
    }

    // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away
    //   constness.
    if (CastsAwayConstness(Self, SrcType, DestType)) {
      Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
        << "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
      return;
    }

    // A valid member pointer cast.
    return;
  }

  // See below for the enumeral issue.
  if (SrcType->isNullPtrType() && DestType->isIntegralType() &&
      !DestType->isEnumeralType()) {
    // C++0x 5.2.10p4: A pointer can be explicitly converted to any integral
    //   type large enough to hold it. A value of std::nullptr_t can be
    //   converted to an integral type; the conversion has the same meaning
    //   and validity as a conversion of (void*)0 to the integral type.
    if (Self.Context.getTypeSize(SrcType) >
        Self.Context.getTypeSize(DestType)) {
      Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
        << OrigDestType << DestRange;
    }
    return;
  }

  bool destIsPtr = DestType->isPointerType();
  bool srcIsPtr = SrcType->isPointerType();
  if (!destIsPtr && !srcIsPtr) {
    // Except for std::nullptr_t->integer and lvalue->reference, which are
    // handled above, at least one of the two arguments must be a pointer.
    Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
      << "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
    return;
  }

  if (SrcType == DestType) {
    // C++ 5.2.10p2 has a note that mentions that, subject to all other
    // restrictions, a cast to the same type is allowed. The intent is not
    // entirely clear here, since all other paragraphs explicitly forbid casts
    // to the same type. However, the behavior of compilers is pretty consistent
    // on this point: allow same-type conversion if the involved types are
    // pointers, disallow otherwise.
    return;
  }

  // Note: Clang treats enumeration types as integral types. If this is ever
  // changed for C++, the additional check here will be redundant.
  if (DestType->isIntegralType() && !DestType->isEnumeralType()) {
    assert(srcIsPtr && "One type must be a pointer");
    // C++ 5.2.10p4: A pointer can be explicitly converted to any integral
    //   type large enough to hold it.
    if (Self.Context.getTypeSize(SrcType) >
        Self.Context.getTypeSize(DestType)) {
      Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
        << OrigDestType << DestRange;
    }
    return;
  }

  if (SrcType->isIntegralType() || SrcType->isEnumeralType()) {
    assert(destIsPtr && "One type must be a pointer");
    // C++ 5.2.10p5: A value of integral or enumeration type can be explicitly
    //   converted to a pointer.
    return;
  }

  if (!destIsPtr || !srcIsPtr) {
    // With the valid non-pointer conversions out of the way, we can be even
    // more stringent.
    Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
      << "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
    return;
  }

  // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
  if (CastsAwayConstness(Self, SrcType, DestType)) {
    Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_const_away)
      << "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
    return;
  }

  // Not casting away constness, so the only remaining check is for compatible
  // pointer categories.

  if (SrcType->isFunctionPointerType()) {
    if (DestType->isFunctionPointerType()) {
      // C++ 5.2.10p6: A pointer to a function can be explicitly converted to
      // a pointer to a function of a different type.
      return;
    }

    // C++0x 5.2.10p8: Converting a pointer to a function into a pointer to
    //   an object type or vice versa is conditionally-supported.
    // Compilers support it in C++03 too, though, because it's necessary for
    // casting the return value of dlsym() and GetProcAddress().
    // FIXME: Conditionally-supported behavior should be configurable in the
    // TargetInfo or similar.
    if (!Self.getLangOptions().CPlusPlus0x) {
      Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
        << OpRange;
    }
    return;
  }

  if (DestType->isFunctionPointerType()) {
    // See above.
    if (!Self.getLangOptions().CPlusPlus0x) {
      Self.Diag(OpRange.getBegin(), diag::ext_reinterpret_cast_fn_obj)
        << OpRange;
    }
    return;
  }

  // C++ 5.2.10p7: A pointer to an object can be explicitly converted to
  //   a pointer to an object of different type.
  // Void pointers are not specified, but supported by every compiler out there.
  // So we finish by allowing everything that remains - it's got to be two
  // object pointers.
}
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");

  // Ignore casts in macros.
  if (CastExpr->getExprLoc().isMacroID())
    return;

  // Casting to void is an idiomatic way to mute "unused variable" and similar
  // warnings.
  if (CastExpr->getCastKind() == CK_ToVoid)
    return;

  auto isFunction = [](QualType T) {
    T = T.getCanonicalType().getNonReferenceType();
    return T->isFunctionType() || T->isFunctionPointerType() ||
           T->isMemberFunctionPointerType();
  };

  const QualType DestTypeAsWritten =
      CastExpr->getTypeAsWritten().getUnqualifiedType();
  const QualType SourceTypeAsWritten =
      CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
  const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
  const QualType DestType = DestTypeAsWritten.getCanonicalType();

  auto ReplaceRange = CharSourceRange::getCharRange(
      CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc());

  bool FnToFnCast =
      isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);

  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
    // Function pointer/reference casts may be needed to resolve ambiguities in
    // case of overloaded functions, so detection of redundant casts is trickier
    // in this case. Don't emit "redundant cast" warnings for function
    // pointer/reference types.
    if (SourceTypeAsWritten == DestTypeAsWritten) {
      diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
          << FixItHint::CreateRemoval(ReplaceRange);
      return;
    }
  }

  // The rest of this check is only relevant to C++.
  // We also disable it for Objective-C++.
  if (!getLangOpts().CPlusPlus || getLangOpts().ObjC1 || getLangOpts().ObjC2)
    return;
  // Ignore code inside extern "C" {} blocks.
  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
           .empty())
    return;
  // Ignore code in .c files and headers included from them, even if they are
  // compiled as C++.
  if (getCurrentMainFile().endswith(".c"))
    return;

  SourceManager &SM = *Result.SourceManager;

  // Ignore code in .c files #included in other files (which shouldn't be done,
  // but people still do this for test and other purposes).
  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c"))
    return;

  // Leave type spelling exactly as it was (unlike
  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
  StringRef DestTypeString =
      Lexer::getSourceText(CharSourceRange::getTokenRange(
                               CastExpr->getLParenLoc().getLocWithOffset(1),
                               CastExpr->getRParenLoc().getLocWithOffset(-1)),
                           SM, getLangOpts());

  auto Diag =
      diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0");

  auto ReplaceWithCast = [&](std::string CastText) {
    const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
    if (!isa<ParenExpr>(SubExpr)) {
      CastText.push_back('(');
      Diag << FixItHint::CreateInsertion(
          Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,
                                     getLangOpts()),
          ")");
    }
    Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
  };
  auto ReplaceWithNamedCast = [&](StringRef CastType) {
    Diag << CastType;
    ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
  };

  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
  switch (CastExpr->getCastKind()) {
  case CK_FunctionToPointerDecay:
    ReplaceWithNamedCast("static_cast");
    return;
  case CK_ConstructorConversion:
    if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
        DestTypeAsWritten->isRecordType() &&
        !DestTypeAsWritten->isElaboratedTypeSpecifier()) {
      Diag << "constructor call syntax";
      // FIXME: Validate DestTypeString, maybe.
      ReplaceWithCast(DestTypeString.str());
    } else {
      ReplaceWithNamedCast("static_cast");
    }
    return;
  case CK_NoOp:
    if (FnToFnCast) {
      ReplaceWithNamedCast("static_cast");
      return;
    }
    if (SourceType == DestType) {
      Diag << "static_cast (if needed, the cast may be redundant)";
      ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
      return;
    }
    if (needsConstCast(SourceType, DestType) &&
        pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
      ReplaceWithNamedCast("const_cast");
      return;
    }
    if (DestType->isReferenceType()) {
      QualType Dest = DestType.getNonReferenceType();
      QualType Source = SourceType.getNonReferenceType();
      if (Source == Dest.withConst() ||
          SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
        ReplaceWithNamedCast("const_cast");
        return;
      }
      break;
    }
  // FALLTHROUGH
  case clang::CK_IntegralCast:
    // Convert integral and no-op casts between builtin types and enums to
    // static_cast. A cast from enum to integer may be unnecessary, but it's
    // still retained.
    if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
        (DestType->isBuiltinType() || DestType->isEnumeralType())) {
      ReplaceWithNamedCast("static_cast");
      return;
    }
    break;
  case CK_BitCast:
    // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
    if (!needsConstCast(SourceType, DestType)) {
      if (SourceType->isVoidPointerType())
        ReplaceWithNamedCast("static_cast");
      else
        ReplaceWithNamedCast("reinterpret_cast");
      return;
    }
    break;
  default:
    break;
  }

  Diag << "static_cast/const_cast/reinterpret_cast";
}