diff options
| author | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-12-19 02:07:47 +0000 |
|---|---|---|
| committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2014-12-19 02:07:47 +0000 |
| commit | a865a1683aeaaeedfb47e0a2d1cb539d513118e5 (patch) | |
| tree | c5c289a3df40cabb219afa3bca08f137cf12bb18 /clang/lib/Sema | |
| parent | aeb50b38053db6b1697447c89b96d3e86c9e7136 (diff) | |
| download | bcm5719-llvm-a865a1683aeaaeedfb47e0a2d1cb539d513118e5.tar.gz bcm5719-llvm-a865a1683aeaaeedfb47e0a2d1cb539d513118e5.zip | |
PR21969: Improve diagnostics for a conversion function that has any pieces of a
declared return type (including a trailing-return-type in C++14).
llvm-svn: 224561
Diffstat (limited to 'clang/lib/Sema')
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 92 |
1 files changed, 88 insertions, 4 deletions
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 96149c5e047..5737901ec6f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6796,6 +6796,22 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, return Context.getFunctionType(Context.VoidTy, None, EPI); } +static void extendLeft(SourceRange &R, const SourceRange &Before) { + if (Before.isInvalid()) + return; + R.setBegin(Before.getBegin()); + if (R.getEnd().isInvalid()) + R.setEnd(Before.getEnd()); +} + +static void extendRight(SourceRange &R, const SourceRange &After) { + if (After.isInvalid()) + return; + if (R.getBegin().isInvalid()) + R.setBegin(After.getBegin()); + R.setEnd(After.getEnd()); +} + /// CheckConversionDeclarator - Called by ActOnDeclarator to check the /// well-formednes of the conversion function declarator @p D with /// type @p R. If there are any errors in the declarator, this routine @@ -6817,7 +6833,9 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R, SC = SC_None; } - QualType ConvType = GetTypeFromParser(D.getName().ConversionFunctionId); + TypeSourceInfo *ConvTSI = nullptr; + QualType ConvType = + GetTypeFromParser(D.getName().ConversionFunctionId, &ConvTSI); if (D.getDeclSpec().hasTypeSpecifier() && !D.isInvalidType()) { // Conversion functions don't have return types, but the parser will @@ -6851,9 +6869,75 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R, // Diagnose "&operator bool()" and other such nonsense. This // is actually a gcc extension which we don't support. if (Proto->getReturnType() != ConvType) { - Diag(D.getIdentifierLoc(), diag::err_conv_function_with_complex_decl) - << Proto->getReturnType(); - D.setInvalidType(); + bool NeedsTypedef = false; + SourceRange Before, After; + + // Walk the chunks and extract information on them for our diagnostic. + bool PastFunctionChunk = false; + for (auto &Chunk : D.type_objects()) { + switch (Chunk.Kind) { + case DeclaratorChunk::Function: + if (!PastFunctionChunk) { + if (Chunk.Fun.HasTrailingReturnType) { + TypeSourceInfo *TRT = nullptr; + GetTypeFromParser(Chunk.Fun.getTrailingReturnType(), &TRT); + if (TRT) extendRight(After, TRT->getTypeLoc().getSourceRange()); + } + PastFunctionChunk = true; + break; + } + // Fall through. + case DeclaratorChunk::Array: + NeedsTypedef = true; + extendRight(After, Chunk.getSourceRange()); + break; + + case DeclaratorChunk::Pointer: + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::Reference: + case DeclaratorChunk::MemberPointer: + extendLeft(Before, Chunk.getSourceRange()); + break; + + case DeclaratorChunk::Paren: + extendLeft(Before, Chunk.Loc); + extendRight(After, Chunk.EndLoc); + break; + } + } + + SourceLocation Loc = Before.isValid() ? Before.getBegin() : + After.isValid() ? After.getBegin() : + D.getIdentifierLoc(); + auto &&DB = Diag(Loc, diag::err_conv_function_with_complex_decl); + DB << Before << After; + + if (!NeedsTypedef) { + DB << /*don't need a typedef*/0; + + // If we can provide a correct fix-it hint, do so. + if (After.isInvalid() && ConvTSI) { + SourceLocation InsertLoc = + PP.getLocForEndOfToken(ConvTSI->getTypeLoc().getLocEnd()); + DB << FixItHint::CreateInsertion(InsertLoc, " ") + << FixItHint::CreateInsertionFromRange( + InsertLoc, CharSourceRange::getTokenRange(Before)) + << FixItHint::CreateRemoval(Before); + } + } else if (!Proto->getReturnType()->isDependentType()) { + DB << /*typedef*/1 << Proto->getReturnType(); + } else if (getLangOpts().CPlusPlus11) { + DB << /*alias template*/2 << Proto->getReturnType(); + } else { + DB << /*might not be fixable*/3; + } + + // Recover by incorporating the other type chunks into the result type. + // Note, this does *not* change the name of the function. This is compatible + // with the GCC extension: + // struct S { &operator int(); } s; + // int &r = s.operator int(); // ok in GCC + // S::operator int&() {} // error in GCC, function name is 'operator int'. ConvType = Proto->getReturnType(); } |

