diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2017-09-29 23:57:25 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2017-09-29 23:57:25 +0000 |
commit | ac63d63543ca824434236ffd788c46eec9339657 (patch) | |
tree | b55ea78a09084f92211b3b4bbd8d7e69bf18260c /clang/lib | |
parent | 1d8cf2be89087a2babc1dc38b16040fad0a555e2 (diff) | |
download | bcm5719-llvm-ac63d63543ca824434236ffd788c46eec9339657.tar.gz bcm5719-llvm-ac63d63543ca824434236ffd788c46eec9339657.zip |
Add a "vexing parse" warning for ambiguity between a variable declaration and a
function-style cast.
This fires for cases such as
T(x);
... where 'x' was previously declared and T is a type. This construct declares
a variable named 'x' rather than the (probably expected) interpretation of a
function-style cast of 'x' to T.
llvm-svn: 314570
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3c455043af3..b12c746b460 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3065,6 +3065,7 @@ static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, S.Diag(D.getCommaLoc(), diag::note_empty_parens_function_call) << FixItHint::CreateReplacement(D.getCommaLoc(), ";") << D.getIdentifier(); + Result.suppressDiagnostics(); } } @@ -3106,6 +3107,99 @@ static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, } } +/// Produce an appropriate diagnostic for a declarator with top-level +/// parentheses. +static void warnAboutRedundantParens(Sema &S, Declarator &D, QualType T) { + DeclaratorChunk &Paren = D.getTypeObject(D.getNumTypeObjects() - 1); + assert(Paren.Kind == DeclaratorChunk::Paren && + "do not have redundant top-level parentheses"); + + // This is a syntactic check; we're not interested in cases that arise + // during template instantiation. + if (S.inTemplateInstantiation()) + return; + + // Check whether this could be intended to be a construction of a temporary + // object in C++ via a function-style cast. + bool CouldBeTemporaryObject = + S.getLangOpts().CPlusPlus && D.isExpressionContext() && + !D.isInvalidType() && D.getIdentifier() && + D.getDeclSpec().getParsedSpecifiers() == DeclSpec::PQ_TypeSpecifier && + (T->isRecordType() || T->isDependentType()) && + D.getDeclSpec().getTypeQualifiers() == 0 && D.isFirstDeclarator(); + + for (auto &C : D.type_objects()) { + switch (C.Kind) { + case DeclaratorChunk::Pointer: + case DeclaratorChunk::Paren: + continue; + + case DeclaratorChunk::Array: + if (!C.Arr.NumElts) + CouldBeTemporaryObject = false; + continue; + + case DeclaratorChunk::Reference: + // FIXME: Suppress the warning here if there is no initializer; we're + // going to give an error anyway. + // We assume that something like 'T (&x) = y;' is highly likely to not + // be intended to be a temporary object. + CouldBeTemporaryObject = false; + continue; + + case DeclaratorChunk::Function: + // In a new-type-id, function chunks require parentheses. + if (D.getContext() == Declarator::CXXNewContext) + return; + LLVM_FALLTHROUGH; + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: + // These cannot appear in expressions. + CouldBeTemporaryObject = false; + continue; + } + } + + // FIXME: If there is an initializer, assume that this is not intended to be + // a construction of a temporary object. + + // Check whether the name has already been declared; if not, this is not a + // function-style cast. + if (CouldBeTemporaryObject) { + LookupResult Result(S, D.getIdentifier(), SourceLocation(), + Sema::LookupOrdinaryName); + if (!S.LookupName(Result, S.getCurScope())) + CouldBeTemporaryObject = false; + Result.suppressDiagnostics(); + } + + SourceRange ParenRange(Paren.Loc, Paren.EndLoc); + + if (!CouldBeTemporaryObject) { + S.Diag(Paren.Loc, diag::warn_redundant_parens_around_declarator) + << ParenRange << FixItHint::CreateRemoval(Paren.Loc) + << FixItHint::CreateRemoval(Paren.EndLoc); + return; + } + + S.Diag(Paren.Loc, diag::warn_parens_disambiguated_as_variable_declaration) + << ParenRange << D.getIdentifier(); + auto *RD = T->getAsCXXRecordDecl(); + if (!RD || !RD->hasDefinition() || RD->hasNonTrivialDestructor()) + S.Diag(Paren.Loc, diag::note_raii_guard_add_name) + << FixItHint::CreateInsertion(Paren.Loc, " varname") << T + << D.getIdentifier(); + // FIXME: A cast to void is probably a better suggestion in cases where it's + // valid (when there is no initializer and we're not in a condition). + S.Diag(D.getLocStart(), diag::note_function_style_cast_add_parentheses) + << FixItHint::CreateInsertion(D.getLocStart(), "(") + << FixItHint::CreateInsertion(S.getLocForEndOfToken(D.getLocEnd()), ")"); + S.Diag(Paren.Loc, diag::note_remove_parens_for_variable_declaration) + << FixItHint::CreateRemoval(Paren.Loc) + << FixItHint::CreateRemoval(Paren.EndLoc); +} + /// Helper for figuring out the default CC for a function declarator type. If /// this is the outermost chunk, then we can determine the CC from the /// declarator context. If not, then this could be either a member function @@ -4007,6 +4101,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, IsQualifiedFunction &= DeclType.Kind == DeclaratorChunk::Paren; switch (DeclType.Kind) { case DeclaratorChunk::Paren: + if (i == 0) + warnAboutRedundantParens(S, D, T); T = S.BuildParenType(T); break; case DeclaratorChunk::BlockPointer: |