[clang] Use RecoveryExprs for broken defaultargs, instead of OpaqueValueExprs

This makes sure we can preserve invalid-ness for consumers of this
node, it prevents crashes. It also aligns better with rest of the places that
store invalid expressions.

Differential Revision: https://reviews.llvm.org/D157868
This commit is contained in:
Kadir Cetinkaya 2023-08-14 15:46:01 +02:00
parent 83695d45d6
commit 373fcd5d73
No known key found for this signature in database
GPG Key ID: E39E36B8D2057ED6
7 changed files with 50 additions and 22 deletions

View File

@ -2543,6 +2543,19 @@ TEST(Hover, All) {
HI.Type = "Test &&";
HI.Definition = "Test &&test = {}";
}},
{
R"cpp(// Shouldn't crash when evaluating the initializer.
struct Bar {}; // error-ok
struct Foo { void foo(Bar x = y); }
void Foo::foo(Bar [[^x]]) {})cpp",
[](HoverInfo &HI) {
HI.Name = "x";
HI.Kind = index::SymbolKind::Parameter;
HI.NamespaceScope = "";
HI.LocalScope = "Foo::foo::";
HI.Type = "Bar";
HI.Definition = "Bar x = <recovery - expr>()";
}},
{
R"cpp(// auto on alias
typedef int int_type;

View File

@ -3022,7 +3022,8 @@ public:
Expr *defarg);
void ActOnParamUnparsedDefaultArgument(Decl *param, SourceLocation EqualLoc,
SourceLocation ArgLoc);
void ActOnParamDefaultArgumentError(Decl *param, SourceLocation EqualLoc);
void ActOnParamDefaultArgumentError(Decl *param, SourceLocation EqualLoc,
Expr* DefaultArg);
ExprResult ConvertParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg,
SourceLocation EqualLoc);
void SetParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg,

View File

@ -395,9 +395,10 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
DefArgResult = ParseBraceInitializer();
} else
DefArgResult = ParseAssignmentExpression();
DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult, Param);
if (DefArgResult.isInvalid()) {
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc,
/*DefaultArg=*/nullptr);
} else {
if (Tok.isNot(tok::eof) || Tok.getEofData() != Param) {
// The last two tokens are the terminator and the saved value of

View File

@ -7548,7 +7548,8 @@ void Parser::ParseParameterDeclarationClause(
} else {
if (Tok.is(tok::l_paren) && NextToken().is(tok::l_brace)) {
Diag(Tok, diag::err_stmt_expr_in_default_arg) << 0;
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc,
/*DefaultArg=*/nullptr);
// Skip the statement expression and continue parsing
SkipUntil(tok::comma, StopBeforeMatch);
continue;
@ -7557,7 +7558,8 @@ void Parser::ParseParameterDeclarationClause(
}
DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
if (DefArgResult.isInvalid()) {
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc,
/*DefaultArg=*/nullptr);
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
} else {
// Inform the actions module about the default argument

View File

@ -20,6 +20,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
@ -37,11 +38,13 @@
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
@ -329,23 +332,16 @@ Sema::ActOnParamDefaultArgument(Decl *param, SourceLocation EqualLoc,
ParmVarDecl *Param = cast<ParmVarDecl>(param);
UnparsedDefaultArgLocs.erase(Param);
auto Fail = [&] {
Param->setInvalidDecl();
Param->setDefaultArg(new (Context) OpaqueValueExpr(
EqualLoc, Param->getType().getNonReferenceType(), VK_PRValue));
};
// Default arguments are only permitted in C++
if (!getLangOpts().CPlusPlus) {
Diag(EqualLoc, diag::err_param_default_argument)
<< DefaultArg->getSourceRange();
return Fail();
return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
}
// Check for unexpanded parameter packs.
if (DiagnoseUnexpandedParameterPack(DefaultArg, UPPC_DefaultArgument)) {
return Fail();
}
if (DiagnoseUnexpandedParameterPack(DefaultArg, UPPC_DefaultArgument))
return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
// C++11 [dcl.fct.default]p3
// A default argument expression [...] shall not be specified for a
@ -360,14 +356,14 @@ Sema::ActOnParamDefaultArgument(Decl *param, SourceLocation EqualLoc,
ExprResult Result = ConvertParamDefaultArgument(Param, DefaultArg, EqualLoc);
if (Result.isInvalid())
return Fail();
return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
DefaultArg = Result.getAs<Expr>();
// Check that the default argument is well-formed
CheckDefaultArgumentVisitor DefaultArgChecker(*this, DefaultArg);
if (DefaultArgChecker.Visit(DefaultArg))
return Fail();
return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
SetParamDefaultArgument(Param, DefaultArg, EqualLoc);
}
@ -389,16 +385,23 @@ void Sema::ActOnParamUnparsedDefaultArgument(Decl *param,
/// ActOnParamDefaultArgumentError - Parsing or semantic analysis of
/// the default argument for the parameter param failed.
void Sema::ActOnParamDefaultArgumentError(Decl *param,
SourceLocation EqualLoc) {
void Sema::ActOnParamDefaultArgumentError(Decl *param, SourceLocation EqualLoc,
Expr *DefaultArg) {
if (!param)
return;
ParmVarDecl *Param = cast<ParmVarDecl>(param);
Param->setInvalidDecl();
UnparsedDefaultArgLocs.erase(Param);
Param->setDefaultArg(new (Context) OpaqueValueExpr(
EqualLoc, Param->getType().getNonReferenceType(), VK_PRValue));
ExprResult RE;
if (DefaultArg) {
RE = CreateRecoveryExpr(EqualLoc, DefaultArg->getEndLoc(), {DefaultArg},
Param->getType().getNonReferenceType());
} else {
RE = CreateRecoveryExpr(EqualLoc, EqualLoc, {},
Param->getType().getNonReferenceType());
}
Param->setDefaultArg(RE.get());
}
/// CheckExtraCXXDefaultArguments - Check for any extra default

View File

@ -0,0 +1,8 @@
// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -ast-dump -frecovery-ast %s | FileCheck %s
void foo();
void fun(int arg = foo());
// CHECK: -ParmVarDecl {{.*}} <col:10, col:24> col:14 invalid arg 'int' cinit
// CHECK-NEXT: -RecoveryExpr {{.*}} <col:18, col:24> 'int' contains-errors
// Make sure we also preserve structure of the errorneous expression
// CHECK: -DeclRefExpr {{.*}} <col:20> 'void ()' {{.*}} 'foo'

View File

@ -79,7 +79,7 @@ int main() {
// CHECK-CC5-NEXT: Objective-C interface
// RUN: c-index-test -code-completion-at=%s:17:11 %s | FileCheck -check-prefix=CHECK-CC6 %s
// CHECK-CC6: FunctionDecl:{ResultType void}{TypedText foo_2}{LeftParen (}{Optional {Placeholder Bar1 b1 = Bar1()}{Optional {Comma , }{Placeholder Bar2 b2}}}{RightParen )} (50)
// CHECK-CC6: FunctionDecl:{ResultType void}{TypedText foo_2}{LeftParen (}{Optional {Placeholder Bar1 b1 = Bar1()}{Optional {Comma , }{Placeholder Bar2 b2 = Bar2()}}}{RightParen )} (50)
// CHECK-CC6: Completion contexts:
// CHECK-CC6-NEXT: Any type
// CHECK-CC6-NEXT: Any value