diff options
Diffstat (limited to 'lib/Sema')
-rw-r--r-- | lib/Sema/DeclSpec.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 58 | ||||
-rw-r--r-- | lib/Sema/SemaType.cpp | 103 |
3 files changed, 107 insertions, 56 deletions
diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index f3ec5656ea..d12ca78390 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -145,6 +145,7 @@ CXXScopeSpec::getWithLocInContext(ASTContext &Context) const { /// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function. /// "TheDeclarator" is the declarator that this will be added to. DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, + bool isAmbiguous, SourceLocation EllipsisLoc, ParamInfo *ArgInfo, unsigned NumArgs, @@ -173,6 +174,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, I.Fun.AttrList = 0; I.Fun.hasPrototype = hasProto; I.Fun.isVariadic = isVariadic; + I.Fun.isAmbiguous = isAmbiguous; I.Fun.EllipsisLoc = EllipsisLoc.getRawEncoding(); I.Fun.DeleteArgInfo = false; I.Fun.TypeQuals = TypeQuals; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 6dfd796f40..e511157366 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5245,58 +5245,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, FunctionTemplate->setInvalidDecl(); } - // If we see "T var();" at block scope, where T is a class type, it is - // probably an attempt to initialize a variable, not a function declaration. - // We don't catch this case earlier, since there is no ambiguity here. - if (!FunctionTemplate && D.getFunctionDefinitionKind() == FDK_Declaration && - CurContext->isFunctionOrMethod() && - D.getNumTypeObjects() == 1 && D.isFunctionDeclarator() && - D.getDeclSpec().getStorageClassSpecAsWritten() - == DeclSpec::SCS_unspecified) { - QualType T = R->getAs<FunctionType>()->getResultType(); - DeclaratorChunk &C = D.getTypeObject(0); - if (!T->isVoidType() && C.Fun.NumArgs == 0 && !C.Fun.isVariadic && - !C.Fun.hasTrailingReturnType() && - C.Fun.getExceptionSpecType() == EST_None) { - SourceRange ParenRange(C.Loc, C.EndLoc); - Diag(C.Loc, diag::warn_empty_parens_are_function_decl) << ParenRange; - - // If the declaration looks like: - // T var1, - // f(); - // and name lookup finds a function named 'f', then the ',' was - // probably intended to be a ';'. - if (!D.isFirstDeclarator() && D.getIdentifier()) { - FullSourceLoc Comma(D.getCommaLoc(), SourceMgr); - FullSourceLoc Name(D.getIdentifierLoc(), SourceMgr); - if (Comma.getFileID() != Name.getFileID() || - Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { - LookupResult Result(*this, D.getIdentifier(), SourceLocation(), - LookupOrdinaryName); - if (LookupName(Result, S)) - Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) - << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << NewFD; - } - } - const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); - // Empty parens mean value-initialization, and no parens mean default - // initialization. These are equivalent if the default constructor is - // user-provided, or if zero-initialization is a no-op. - if (RD && RD->hasDefinition() && - (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) - Diag(C.Loc, diag::note_empty_parens_default_ctor) - << FixItHint::CreateRemoval(ParenRange); - else { - std::string Init = getFixItZeroInitializerForType(T); - if (Init.empty() && LangOpts.CPlusPlus0x) - Init = "{}"; - if (!Init.empty()) - Diag(C.Loc, diag::note_empty_parens_zero_initialize) - << FixItHint::CreateReplacement(ParenRange, Init); - } - } - } - // C++ [dcl.fct.spec]p5: // The virtual specifier shall only be used in declarations of // nonstatic class member functions that appear within a @@ -7915,10 +7863,10 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, (void)Error; // Silence warning. assert(!Error && "Error setting up implicit decl!"); Declarator D(DS, Declarator::BlockContext); - D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, - 0, 0, true, SourceLocation(), + D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, false, + SourceLocation(), 0, 0, 0, true, + SourceLocation(), SourceLocation(), SourceLocation(), SourceLocation(), - SourceLocation(), EST_None, SourceLocation(), 0, 0, 0, 0, Loc, Loc, D), DS.getAttributes(), diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 84e8a7944d..fcea5a75a9 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -554,7 +554,8 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, // ...and *prepend* it to the declarator. declarator.AddInnermostTypeInfo(DeclaratorChunk::getFunction( /*proto*/ true, - /*variadic*/ false, SourceLocation(), + /*variadic*/ false, + /*ambiguous*/ false, SourceLocation(), /*args*/ 0, 0, /*type quals*/ 0, /*ref-qualifier*/true, SourceLocation(), @@ -2040,6 +2041,101 @@ static void checkQualifiedFunction(Sema &S, QualType T, << getFunctionQualifiersAsString(T->castAs<FunctionProtoType>()); } +/// Produce an approprioate diagnostic for an ambiguity between a function +/// declarator and a C++ direct-initializer. +static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, + DeclaratorChunk &DeclType, QualType RT) { + const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun; + assert(FTI.isAmbiguous && "no direct-initializer / function ambiguity"); + + // If the return type is void there is no ambiguity. + if (RT->isVoidType()) + return; + + // An initializer for a non-class type can have at most one argument. + if (!RT->isRecordType() && FTI.NumArgs > 1) + return; + + // An initializer for a reference must have exactly one argument. + if (RT->isReferenceType() && FTI.NumArgs != 1) + return; + + // Only warn if this declarator is declaring a function at block scope, and + // doesn't have a storage class (such as 'extern') specified. + if (!D.isFunctionDeclarator() || + D.getFunctionDefinitionKind() != FDK_Declaration || + !S.CurContext->isFunctionOrMethod() || + D.getDeclSpec().getStorageClassSpecAsWritten() + != DeclSpec::SCS_unspecified) + return; + + // Inside a condition, a direct initializer is not permitted. We allow one to + // be parsed in order to give better diagnostics in condition parsing. + if (D.getContext() == Declarator::ConditionContext) + return; + + SourceRange ParenRange(DeclType.Loc, DeclType.EndLoc); + + // Declaration with parameters, eg. "T var(T());". + if (FTI.NumArgs > 0 && + D.getContext() != Declarator::ConditionContext) { + S.Diag(DeclType.Loc, + diag::warn_parens_disambiguated_as_function_declaration) + << ParenRange; + + SourceRange Range = FTI.ArgInfo[0].Param->getSourceRange(); + SourceLocation B = Range.getBegin(); + SourceLocation E = S.PP.getLocForEndOfToken(Range.getEnd()); + // FIXME: Maybe we should suggest adding braces instead of parens + // in C++11 for classes that don't have an initializer_list constructor. + S.Diag(B, diag::note_additional_parens_for_variable_declaration) + << FixItHint::CreateInsertion(B, "(") + << FixItHint::CreateInsertion(E, ")"); + } + + // Declaration without parameters, eg. "T var();". + if (FTI.NumArgs == 0) { + S.Diag(DeclType.Loc, diag::warn_empty_parens_are_function_decl) + << ParenRange; + + // If the declaration looks like: + // T var1, + // f(); + // and name lookup finds a function named 'f', then the ',' was + // probably intended to be a ';'. + if (!D.isFirstDeclarator() && D.getIdentifier()) { + FullSourceLoc Comma(D.getCommaLoc(), S.SourceMgr); + FullSourceLoc Name(D.getIdentifierLoc(), S.SourceMgr); + if (Comma.getFileID() != Name.getFileID() || + Comma.getSpellingLineNumber() != Name.getSpellingLineNumber()) { + LookupResult Result(S, D.getIdentifier(), SourceLocation(), + Sema::LookupOrdinaryName); + if (S.LookupName(Result, S.getCurScope())) + S.Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) + << FixItHint::CreateReplacement(D.getCommaLoc(), ";") + << D.getIdentifier(); + } + } + const CXXRecordDecl *RD = RT->getAsCXXRecordDecl(); + // Empty parens mean value-initialization, and no parens mean + // default initialization. These are equivalent if the default + // constructor is user-provided or if zero-initialization is a + // no-op. + if (RD && RD->hasDefinition() && + (RD->isEmpty() || RD->hasUserProvidedDefaultConstructor())) + S.Diag(DeclType.Loc, diag::note_empty_parens_default_ctor) + << FixItHint::CreateRemoval(ParenRange); + else { + std::string Init = S.getFixItZeroInitializerForType(RT); + if (Init.empty() && S.LangOpts.CPlusPlus0x) + Init = "{}"; + if (!Init.empty()) + S.Diag(DeclType.Loc, diag::note_empty_parens_zero_initialize) + << FixItHint::CreateReplacement(ParenRange, Init); + } + } +} + static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -2272,6 +2368,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, << (D.getContext() == Declarator::AliasDeclContext || D.getContext() == Declarator::AliasTemplateContext); + // If we see "T var();" or "T var(T());" at block scope, it is probably + // an attempt to initialize a variable, not a function declaration. + if (FTI.isAmbiguous) + warnAboutAmbiguousFunction(S, D, DeclType, T); + if (!FTI.NumArgs && !FTI.isVariadic && !LangOpts.CPlusPlus) { // Simple void foo(), where the incoming T is the result type. T = Context.getFunctionNoProtoType(T); |