Introduce a FunctionDecl::getReturnTypeSourceRange() utility

This source range is useful for all kinds of diagnostic QOI and refactoring
work, so let's make it more discoverable.

This commit also makes use of the new function to enhance various diagnostics
relating to return types and resolves an old FIXME.

llvm-svn: 212154
This commit is contained in:
Alp Toker 2014-07-02 01:47:15 +00:00
parent 5b336a242c
commit d0787ebf5e
4 changed files with 62 additions and 50 deletions

View File

@ -1896,6 +1896,8 @@ public:
return getType()->getAs<FunctionType>()->getReturnType(); return getType()->getAs<FunctionType>()->getReturnType();
} }
SourceRange getReturnTypeSourceRange() const;
/// \brief Determine the type of an expression that calls this function. /// \brief Determine the type of an expression that calls this function.
QualType getCallResultType() const { QualType getCallResultType() const {
return getType()->getAs<FunctionType>()->getCallResultType(getASTContext()); return getType()->getAs<FunctionType>()->getCallResultType(getASTContext());

View File

@ -2687,6 +2687,23 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const {
return FoundBody; return FoundBody;
} }
SourceRange FunctionDecl::getReturnTypeSourceRange() const {
const TypeSourceInfo *TSI = getTypeSourceInfo();
if (!TSI)
return SourceRange();
TypeLoc TL = TSI->getTypeLoc();
FunctionTypeLoc FunctionTL = TL.getAs<FunctionTypeLoc>();
if (!FunctionTL)
return SourceRange();
TypeLoc ResultTL = FunctionTL.getReturnLoc();
if (ResultTL.getUnqualifiedLoc().getAs<BuiltinTypeLoc>())
return ResultTL.getSourceRange();
return SourceRange();
}
/// \brief For an inline function definition in C, or for a gnu_inline function /// \brief For an inline function definition in C, or for a gnu_inline function
/// in C++, determine whether the definition will be externally visible. /// in C++, determine whether the definition will be externally visible.
/// ///

View File

@ -2519,11 +2519,13 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType); ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType);
if (ResQT.isNull()) { if (ResQT.isNull()) {
if (New->isCXXClassMember() && New->isOutOfLine()) if (New->isCXXClassMember() && New->isOutOfLine())
Diag(New->getLocation(), Diag(New->getLocation(), diag::err_member_def_does_not_match_ret_type)
diag::err_member_def_does_not_match_ret_type) << New; << New << New->getReturnTypeSourceRange();
else else
Diag(New->getLocation(), diag::err_ovl_diff_return_type); Diag(New->getLocation(), diag::err_ovl_diff_return_type)
Diag(OldLocation, PrevDiag) << Old << Old->getType(); << New->getReturnTypeSourceRange();
Diag(OldLocation, PrevDiag) << Old << Old->getType()
<< Old->getReturnTypeSourceRange();
return true; return true;
} }
else else
@ -7494,8 +7496,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// OpenCL v1.2, s6.9 -- Kernels can only have return type void. // OpenCL v1.2, s6.9 -- Kernels can only have return type void.
if (!NewFD->getReturnType()->isVoidType()) { if (!NewFD->getReturnType()->isVoidType()) {
Diag(D.getIdentifierLoc(), SourceRange RTRange = NewFD->getReturnTypeSourceRange();
diag::err_expected_kernel_void_return_type); Diag(D.getIdentifierLoc(), diag::err_expected_kernel_void_return_type)
<< (RTRange.isValid() ? FixItHint::CreateReplacement(RTRange, "void")
: FixItHint());
D.setInvalidType(); D.setInvalidType();
} }
@ -7835,23 +7839,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
return Redeclaration; return Redeclaration;
} }
static SourceRange getResultSourceRange(const FunctionDecl *FD) {
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
if (!TSI)
return SourceRange();
TypeLoc TL = TSI->getTypeLoc();
FunctionTypeLoc FunctionTL = TL.getAs<FunctionTypeLoc>();
if (!FunctionTL)
return SourceRange();
TypeLoc ResultTL = FunctionTL.getReturnLoc();
if (ResultTL.getUnqualifiedLoc().getAs<BuiltinTypeLoc>())
return ResultTL.getSourceRange();
return SourceRange();
}
void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) { void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
// C++11 [basic.start.main]p3: // C++11 [basic.start.main]p3:
// A program that [...] declares main to be inline, static or // A program that [...] declares main to be inline, static or
@ -7904,19 +7891,17 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
} else if (getLangOpts().GNUMode && !getLangOpts().CPlusPlus) { } else if (getLangOpts().GNUMode && !getLangOpts().CPlusPlus) {
Diag(FD->getTypeSpecStartLoc(), diag::ext_main_returns_nonint); Diag(FD->getTypeSpecStartLoc(), diag::ext_main_returns_nonint);
SourceRange ResultRange = getResultSourceRange(FD); SourceRange RTRange = FD->getReturnTypeSourceRange();
if (ResultRange.isValid()) if (RTRange.isValid())
Diag(ResultRange.getBegin(), diag::note_main_change_return_type) Diag(RTRange.getBegin(), diag::note_main_change_return_type)
<< FixItHint::CreateReplacement(ResultRange, "int"); << FixItHint::CreateReplacement(RTRange, "int");
// Otherwise, this is just a flat-out error. // Otherwise, this is just a flat-out error.
} else { } else {
SourceRange ResultRange = getResultSourceRange(FD); SourceRange RTRange = FD->getReturnTypeSourceRange();
if (ResultRange.isValid()) Diag(FD->getTypeSpecStartLoc(), diag::err_main_returns_nonint)
Diag(FD->getTypeSpecStartLoc(), diag::err_main_returns_nonint) << (RTRange.isValid() ? FixItHint::CreateReplacement(RTRange, "int")
<< FixItHint::CreateReplacement(ResultRange, "int"); : FixItHint());
else
Diag(FD->getTypeSpecStartLoc(), diag::err_main_returns_nonint);
FD->setInvalidDecl(true); FD->setInvalidDecl(true);
} }

View File

@ -12304,8 +12304,10 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
if (NewClassTy.isNull()) { if (NewClassTy.isNull()) {
Diag(New->getLocation(), Diag(New->getLocation(),
diag::err_different_return_type_for_overriding_virtual_function) diag::err_different_return_type_for_overriding_virtual_function)
<< New->getDeclName() << NewTy << OldTy; << New->getDeclName() << NewTy << OldTy
Diag(Old->getLocation(), diag::note_overridden_virtual_function); << New->getReturnTypeSourceRange();
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
return true; return true;
} }
@ -12325,25 +12327,27 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
if (!Context.hasSameUnqualifiedType(NewClassTy, OldClassTy)) { if (!Context.hasSameUnqualifiedType(NewClassTy, OldClassTy)) {
// Check if the new class derives from the old class. // Check if the new class derives from the old class.
if (!IsDerivedFrom(NewClassTy, OldClassTy)) { if (!IsDerivedFrom(NewClassTy, OldClassTy)) {
Diag(New->getLocation(), Diag(New->getLocation(), diag::err_covariant_return_not_derived)
diag::err_covariant_return_not_derived) << New->getDeclName() << NewTy << OldTy
<< New->getDeclName() << NewTy << OldTy; << New->getReturnTypeSourceRange();
Diag(Old->getLocation(), diag::note_overridden_virtual_function); Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
return true; return true;
} }
// Check if we the conversion from derived to base is valid. // Check if we the conversion from derived to base is valid.
if (CheckDerivedToBaseConversion(NewClassTy, OldClassTy, if (CheckDerivedToBaseConversion(
diag::err_covariant_return_inaccessible_base, NewClassTy, OldClassTy,
diag::err_covariant_return_ambiguous_derived_to_base_conv, diag::err_covariant_return_inaccessible_base,
// FIXME: Should this point to the return type? diag::err_covariant_return_ambiguous_derived_to_base_conv,
New->getLocation(), SourceRange(), New->getDeclName(), New->getLocation(), New->getReturnTypeSourceRange(),
nullptr)) { New->getDeclName(), nullptr)) {
// FIXME: this note won't trigger for delayed access control // FIXME: this note won't trigger for delayed access control
// diagnostics, and it's impossible to get an undelayed error // diagnostics, and it's impossible to get an undelayed error
// here from access control during the original parse because // here from access control during the original parse because
// the ParsingDeclSpec/ParsingDeclarator are still in scope. // the ParsingDeclSpec/ParsingDeclarator are still in scope.
Diag(Old->getLocation(), diag::note_overridden_virtual_function); Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
return true; return true;
} }
} }
@ -12352,8 +12356,10 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
if (NewTy.getLocalCVRQualifiers() != OldTy.getLocalCVRQualifiers()) { if (NewTy.getLocalCVRQualifiers() != OldTy.getLocalCVRQualifiers()) {
Diag(New->getLocation(), Diag(New->getLocation(),
diag::err_covariant_return_type_different_qualifications) diag::err_covariant_return_type_different_qualifications)
<< New->getDeclName() << NewTy << OldTy; << New->getDeclName() << NewTy << OldTy
Diag(Old->getLocation(), diag::note_overridden_virtual_function); << New->getReturnTypeSourceRange();
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
return true; return true;
}; };
@ -12362,8 +12368,10 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
if (NewClassTy.isMoreQualifiedThan(OldClassTy)) { if (NewClassTy.isMoreQualifiedThan(OldClassTy)) {
Diag(New->getLocation(), Diag(New->getLocation(),
diag::err_covariant_return_type_class_type_more_qualified) diag::err_covariant_return_type_class_type_more_qualified)
<< New->getDeclName() << NewTy << OldTy; << New->getDeclName() << NewTy << OldTy
Diag(Old->getLocation(), diag::note_overridden_virtual_function); << New->getReturnTypeSourceRange();
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
return true; return true;
}; };