summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2017-09-29 23:57:25 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2017-09-29 23:57:25 +0000
commitac63d63543ca824434236ffd788c46eec9339657 (patch)
treeb55ea78a09084f92211b3b4bbd8d7e69bf18260c /clang
parent1d8cf2be89087a2babc1dc38b16040fad0a555e2 (diff)
downloadbcm5719-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')
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td12
-rw-r--r--clang/include/clang/Sema/DeclSpec.h36
-rw-r--r--clang/lib/Sema/SemaType.cpp96
-rw-r--r--clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp1
-rw-r--r--clang/test/FixIt/fixit-vexing-parse.cpp21
-rw-r--r--clang/test/Parser/cxx0x-condition.cpp4
-rw-r--r--clang/test/Parser/cxx1z-class-template-argument-deduction.cpp1
-rw-r--r--clang/test/SemaCXX/decl-expr-ambiguity.cpp24
8 files changed, 193 insertions, 2 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d648d2f45f4..38f2a3bd655 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -295,8 +295,20 @@ def warn_empty_parens_are_function_decl : Warning<
def warn_parens_disambiguated_as_function_declaration : Warning<
"parentheses were disambiguated as a function declaration">,
InGroup<VexingParse>;
+def warn_parens_disambiguated_as_variable_declaration : Warning<
+ "parentheses were disambiguated as redundant parentheses around declaration "
+ "of variable named %0">, InGroup<VexingParse>;
+def warn_redundant_parens_around_declarator : Warning<
+ "redundant parentheses surrounding declarator">,
+ InGroup<DiagGroup<"redundant-parens">>, DefaultIgnore;
def note_additional_parens_for_variable_declaration : Note<
"add a pair of parentheses to declare a variable">;
+def note_raii_guard_add_name : Note<
+ "add a variable name to declare a %0 initialized with %1">;
+def note_function_style_cast_add_parentheses : Note<
+ "add enclosing parentheses to perform a function-style cast">;
+def note_remove_parens_for_variable_declaration : Note<
+ "remove parentheses to silence this warning">;
def note_empty_parens_function_call : Note<
"change this ',' to a ';' to call %0">;
def note_empty_parens_default_ctor : Note<
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 1d935df433f..4fc55a1daca 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2301,6 +2301,42 @@ public:
}
llvm_unreachable("unknown context kind!");
}
+
+ /// Determine whether this declaration appears in a context where an
+ /// expression could appear.
+ bool isExpressionContext() const {
+ switch (Context) {
+ case FileContext:
+ case KNRTypeListContext:
+ case MemberContext:
+ case TypeNameContext: // FIXME: sizeof(...) permits an expression.
+ case FunctionalCastContext:
+ case AliasDeclContext:
+ case AliasTemplateContext:
+ case PrototypeContext:
+ case LambdaExprParameterContext:
+ case ObjCParameterContext:
+ case ObjCResultContext:
+ case TemplateParamContext:
+ case CXXNewContext:
+ case CXXCatchContext:
+ case ObjCCatchContext:
+ case BlockLiteralContext:
+ case LambdaExprContext:
+ case ConversionIdContext:
+ case TrailingReturnContext:
+ return false;
+
+ case BlockContext:
+ case ForContext:
+ case InitStmtContext:
+ case ConditionContext:
+ case TemplateTypeArgContext:
+ return true;
+ }
+
+ llvm_unreachable("unknown context kind!");
+ }
/// \brief Return true if a function declarator at this position would be a
/// function declaration.
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:
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
index 18b18834bb4..cf58a85d849 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp
@@ -73,6 +73,7 @@ void other_contexts() {
int a;
{
X0::X0(a); // expected-error{{qualified reference to 'X0' is a constructor name rather than a type in this context}}
+ // expected-warning@-1 {{redundant parentheses around declaration of variable named 'a'}} expected-note@-1 2{{}}
}
}
diff --git a/clang/test/FixIt/fixit-vexing-parse.cpp b/clang/test/FixIt/fixit-vexing-parse.cpp
index 71d3eff5329..973c6961a08 100644
--- a/clang/test/FixIt/fixit-vexing-parse.cpp
+++ b/clang/test/FixIt/fixit-vexing-parse.cpp
@@ -106,3 +106,24 @@ namespace N {
wchar_t wc(); // expected-warning {{function declaration}} expected-note {{replace parentheses with an initializer}}
}
}
+
+namespace RedundantParens {
+struct Y {
+ Y();
+ Y(int);
+ ~Y();
+};
+int n;
+
+void test() {
+ // CHECK: add a variable name
+ // CHECK: fix-it:"{{.*}}":{[[@LINE+7]]:4-[[@LINE+7]]:4}:" varname"
+ // CHECK: add enclosing parentheses
+ // CHECK: fix-it:"{{.*}}":{[[@LINE+5]]:3-[[@LINE+5]]:3}:"("
+ // CHECK: fix-it:"{{.*}}":{[[@LINE+4]]:7-[[@LINE+4]]:7}:")"
+ // CHECK: remove parentheses
+ // CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:4-[[@LINE+2]]:5}:" "
+ // CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:6-[[@LINE+1]]:7}:""
+ Y(n); // expected-warning {{declaration of variable named 'n'}} expected-note 3{{}}
+}
+}
diff --git a/clang/test/Parser/cxx0x-condition.cpp b/clang/test/Parser/cxx0x-condition.cpp
index 071e09e4158..19d5a7395d4 100644
--- a/clang/test/Parser/cxx0x-condition.cpp
+++ b/clang/test/Parser/cxx0x-condition.cpp
@@ -14,11 +14,11 @@ void f() {
for (; int x = ++a; ) ;
if (S(a)) {} // ok
- if (S(a) = 0) {} // ok
+ if (S(a) = 0) {} // expected-warning {{redundant parentheses}} expected-note 2{{}}
if (S(a) == 0) {} // ok
if (S(n)) {} // expected-error {{unexpected type name 'n': expected expression}}
- if (S(n) = 0) {} // ok
+ if (S(n) = 0) {} // expected-warning {{redundant parentheses}} expected-note 2{{}}
if (S(n) == 0) {} // expected-error {{unexpected type name 'n': expected expression}}
if (S b(a)) {} // expected-error {{variable declaration in condition cannot have a parenthesized initializer}}
diff --git a/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp b/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
index dac17dfbf48..b8f5a82d461 100644
--- a/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
+++ b/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
@@ -180,6 +180,7 @@ namespace typename_specifier {
{(void)(typename T::A)(0);} // expected-error{{refers to class template member}}
{(void)(typename T::A){0};} // expected-error{{refers to class template member}}
{typename T::A (parens) = 0;} // expected-error {{refers to class template member in 'typename_specifier::X'; argument deduction not allowed here}}
+ // expected-warning@-1 {{disambiguated as redundant parentheses around declaration of variable named 'parens'}} expected-note@-1 {{add a variable name}} expected-note@-1{{remove parentheses}} expected-note@-1 {{add enclosing parentheses}}
{typename T::A *p = 0;} // expected-error {{refers to class template member}}
{typename T::A &r = *p;} // expected-error {{refers to class template member}}
{typename T::A arr[3] = 0;} // expected-error {{refers to class template member}}
diff --git a/clang/test/SemaCXX/decl-expr-ambiguity.cpp b/clang/test/SemaCXX/decl-expr-ambiguity.cpp
index 1b1f7dc8a70..1e31d701d0c 100644
--- a/clang/test/SemaCXX/decl-expr-ambiguity.cpp
+++ b/clang/test/SemaCXX/decl-expr-ambiguity.cpp
@@ -48,13 +48,20 @@ void f() {
struct RAII {
RAII();
+ RAII(int);
~RAII();
};
+struct NotRAII {
+ NotRAII();
+ NotRAII(int);
+};
+
void func();
void func2(short);
namespace N {
struct S;
+ int n;
void emptyParens() {
RAII raii(); // expected-warning {{function declaration}} expected-note {{remove parentheses to declare a variable}}
@@ -69,6 +76,23 @@ namespace N {
void nonEmptyParens() {
int f = 0, // g = 0; expected-note {{change this ',' to a ';' to call 'func2'}}
func2(short(f)); // expected-warning {{function declaration}} expected-note {{add a pair of parentheses}}
+
+ RAII(n); // expected-warning {{parentheses were disambiguated as redundant parentheses around declaration of variable named 'n'}}
+ // expected-note@-1 {{add a variable name to declare a 'RAII' initialized with 'n'}}
+ // expected-note@-2 {{add enclosing parentheses to perform a function-style cast}}
+ // expected-note@-3 {{remove parentheses to silence this warning}}
+
+ RAII(undeclared1);
+#pragma clang diagnostic push
+#pragma clang diagnostic warning "-Wredundant-parens"
+ RAII(undeclared2); // expected-warning {{redundant parentheses surrounding declarator}}
+#pragma clang diagnostic pop
+
+ {
+ NotRAII(n); // expected-warning {{parentheses were disambiguated as redundant parentheses around declaration of variable named 'n'}}
+ // expected-note@-1 {{add enclosing parentheses to perform a function-style cast}}
+ // expected-note@-2 {{remove parentheses to silence this warning}}
+ }
}
}
OpenPOWER on IntegriCloud