From 7da4d8ce68c106616260aae0415ce1e48ea36a23 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Sat, 18 Jul 2020 16:12:45 -0600 Subject: [PATCH 01/10] Added note and logic for IsProbablyStatement. --- include/swift/AST/DiagnosticsParse.def | 1 + lib/Parse/ParseDecl.cpp | 9 +++++++++ test/Parse/diagnostic_missing_func_keyword.swift | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 6c7f4e6533b04..5922e830f2157 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -217,6 +217,7 @@ ERROR(let_cannot_be_addressed_property,none, ERROR(disallowed_var_multiple_getset,none, "'var' declarations with multiple variables cannot have explicit" " getters/setters", ()) +NOTE(found_unexpected_stmt,none, "found an unexpected statement here", ()) ERROR(disallowed_init,none, "initial value is not allowed here", ()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index aa3cf8ce5cad1..2c0417a85e1df 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3903,6 +3903,15 @@ Parser::parseDecl(ParseDeclOptions Flags, const bool IsProbablyTupleDecl = Tok.is(tok::l_paren) && peekToken().isIdentifierOrUnderscore(); + const bool IsProbablyStatement = + Tok.isIdentifierOrUnderscore() && + peekToken().isAny(tok::period); + + if (IsProbablyStatement) { + diagnose(Tok.getLoc(), diag::found_unexpected_stmt); + break; + } + if (IsProbablyVarDecl || IsProbablyTupleDecl) { DescriptiveDeclKind DescriptiveKind; diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 295f9d73eea4b..22795a64b484b 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -38,7 +38,11 @@ struct Bar { (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} +} + +struct Faz { + abc : Int = 10 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + def.ber : Float = 1.0 // expected-note {{found an unexpected statement here}} } class Baz { From b84980022b2ebe707bf06a99370c8ad08efa6505 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Sun, 19 Jul 2020 08:44:12 -0600 Subject: [PATCH 02/10] Replaced one line in unit tests that was inadvertently removed. --- test/Parse/diagnostic_missing_func_keyword.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 22795a64b484b..6d7981cc89d8b 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -38,6 +38,7 @@ struct Bar { (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} } struct Faz { From baf14e6ead2176ad33d9e786a94e23a12a80380e Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Sun, 19 Jul 2020 17:06:12 -0600 Subject: [PATCH 03/10] Changed logic for IsProbablyFuncDecl to require left-parenthesis or left-angle-bracket. Edited tests to expect 'Expected declaration'. --- lib/Parse/ParseDecl.cpp | 12 ++---------- test/Parse/diagnostic_missing_func_keyword.swift | 12 +++++++----- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 2c0417a85e1df..a4e0bd485902c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3903,15 +3903,6 @@ Parser::parseDecl(ParseDeclOptions Flags, const bool IsProbablyTupleDecl = Tok.is(tok::l_paren) && peekToken().isIdentifierOrUnderscore(); - const bool IsProbablyStatement = - Tok.isIdentifierOrUnderscore() && - peekToken().isAny(tok::period); - - if (IsProbablyStatement) { - diagnose(Tok.getLoc(), diag::found_unexpected_stmt); - break; - } - if (IsProbablyVarDecl || IsProbablyTupleDecl) { DescriptiveDeclKind DescriptiveKind; @@ -3936,7 +3927,8 @@ Parser::parseDecl(ParseDeclOptions Flags, } const bool IsProbablyFuncDecl = - Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator(); + (Tok.isIdentifierOrUnderscore() && peekToken().isAny(tok::l_paren, tok::l_angle)) || + (Tok.isAnyOperator() && peekToken().isAny(tok::l_paren, tok::l_angle)); if (IsProbablyFuncDecl) { diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 6d7981cc89d8b..d1f312582a871 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -27,9 +27,9 @@ infix operator %% struct Bar { fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} - // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} - // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} + %% (lhs: T, rhs: T) -> T { // expected-error {{expected declaration}} + + lhs + lhs + rhs + rhs } @@ -38,12 +38,14 @@ struct Bar { (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + a, b: Int // expected-error {{expected declaration}} } struct Faz { abc : Int = 10 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - def.ber : Float = 1.0 // expected-note {{found an unexpected statement here}} + def.ber : Float = 1.0 // expected-error {{expected declaration}} + + x + 3 // expected-error {{expected declaration}} } class Baz { From e4845696d9627eba0f0f536fa6394478991557b2 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Mon, 20 Jul 2020 14:03:20 -0600 Subject: [PATCH 04/10] Reverted to a simple fail-fast on identifier followed by period. --- lib/Parse/ParseDecl.cpp | 13 +++++++++++-- test/Parse/diagnostic_missing_func_keyword.swift | 16 +++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index a4e0bd485902c..48f97fc041295 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3890,6 +3890,16 @@ Parser::parseDecl(ParseDeclOptions Flags, if (Flags.contains(PD_HasContainerType) && IsAtStartOfLineOrPreviousHadSemi) { + // Fail fast if we have something that starts with an identifier + // but cannot be a var or func declaration. + const bool NotAVarOrFuncDecl = Tok.isIdentifierOrUnderscore() && + peekToken().isAny(tok::period); + + if (NotAVarOrFuncDecl) { + diagnose(Tok, diag::expected_decl); + break; + } + // Emit diagnostics if we meet an identifier/operator where a declaration // is expected, perhaps the user forgot the 'func' or 'var' keyword. // @@ -3927,8 +3937,7 @@ Parser::parseDecl(ParseDeclOptions Flags, } const bool IsProbablyFuncDecl = - (Tok.isIdentifierOrUnderscore() && peekToken().isAny(tok::l_paren, tok::l_angle)) || - (Tok.isAnyOperator() && peekToken().isAny(tok::l_paren, tok::l_angle)); + Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator(); if (IsProbablyFuncDecl) { diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index d1f312582a871..15e715de71a41 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -27,9 +27,9 @@ infix operator %% struct Bar { fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - %% (lhs: T, rhs: T) -> T { // expected-error {{expected declaration}} - - + %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} + // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} + // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} lhs + lhs + rhs + rhs } @@ -38,14 +38,12 @@ struct Bar { (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - a, b: Int // expected-error {{expected declaration}} -} + a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} -struct Faz { - abc : Int = 10 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - def.ber : Float = 1.0 // expected-error {{expected declaration}} + a.qux = 345 // expected-error {{expected declaration}} + + fisr.qux = 345 // expected-error {{expected declaration}} - x + 3 // expected-error {{expected declaration}} } class Baz { From 95014143b701e99941ebe9cfd870bd55e122fc17 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Tue, 21 Jul 2020 21:32:55 -0600 Subject: [PATCH 05/10] Added parseExpr call to try to parse over identifier-period statements in type-level body. Removed one redundant note. Clarified two unit tests. --- include/swift/AST/DiagnosticsParse.def | 1 - lib/Parse/ParseDecl.cpp | 17 ++++++++++++----- .../Parse/diagnostic_missing_func_keyword.swift | 8 +++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 5922e830f2157..6c7f4e6533b04 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -217,7 +217,6 @@ ERROR(let_cannot_be_addressed_property,none, ERROR(disallowed_var_multiple_getset,none, "'var' declarations with multiple variables cannot have explicit" " getters/setters", ()) -NOTE(found_unexpected_stmt,none, "found an unexpected statement here", ()) ERROR(disallowed_init,none, "initial value is not allowed here", ()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 48f97fc041295..ce8fb5c5d4375 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3891,13 +3891,20 @@ Parser::parseDecl(ParseDeclOptions Flags, IsAtStartOfLineOrPreviousHadSemi) { // Fail fast if we have something that starts with an identifier - // but cannot be a var or func declaration. - const bool NotAVarOrFuncDecl = Tok.isIdentifierOrUnderscore() && + // and a period, but cannot be a var or func declaration. + // we try to parse from here as an expression to pass over + // the tokens and reach a safe place to keep parsing. + const bool IdentifierPeriod = Tok.isIdentifierOrUnderscore() && peekToken().isAny(tok::period); - if (NotAVarOrFuncDecl) { - diagnose(Tok, diag::expected_decl); - break; + if (IdentifierPeriod) { + ParserResult ResultExpr = parseExpr(diag::expected_expr); + // parse the rest of the expression to get past the problem. + if (ResultExpr.isNonNull()) { + auto Result = ResultExpr.get(); + } + diagnose(Tok, diag::expected_decl); + break; } // Emit diagnostics if we meet an identifier/operator where a declaration diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 15e715de71a41..4d67d6f76e03d 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -40,9 +40,11 @@ struct Bar { a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - a.qux = 345 // expected-error {{expected declaration}} - - fisr.qux = 345 // expected-error {{expected declaration}} + // ensure that the id.id pattern is not interpreted as a function + a.foo = 345 // expected-error {{expected declaration}} + // ensure that the id.id pattern generating an expected declaration + // diagnostic does not block further diagnostics. + fisr.bar = 345 // expected-error {{expected declaration}} } From 92bb52cd2567927d0da882b000585b169a4e3dba Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Thu, 23 Jul 2020 12:42:37 -0600 Subject: [PATCH 06/10] Returned a parser error when we find the IdentifierPeriod condition. This still swallows up any following expression because on parser error, parseDeclItem skips to the following right brace, swallowing up anything after our expression. --- lib/Parse/ParseDecl.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ce8fb5c5d4375..03748a3fb9de4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3898,13 +3898,12 @@ Parser::parseDecl(ParseDeclOptions Flags, peekToken().isAny(tok::period); if (IdentifierPeriod) { - ParserResult ResultExpr = parseExpr(diag::expected_expr); - // parse the rest of the expression to get past the problem. - if (ResultExpr.isNonNull()) { - auto Result = ResultExpr.get(); - } + // diagnostic first before parseExpr moves the cursor diagnose(Tok, diag::expected_decl); - break; + // parse the rest of the expression to get past the problem. + parseExpr(diag::expected_expr); + // we did not parse a Decl here so we need to report an error + return makeParserError(); } // Emit diagnostics if we meet an identifier/operator where a declaration From 608ac924419e397fa15b835477cd992f2d5ee25a Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Thu, 23 Jul 2020 20:55:35 -0600 Subject: [PATCH 07/10] Removed an expected-error from a test to ensure it passes because I can't get the compiler to continue parsing after an expression in the middle of a struct definition. --- lib/Parse/ParseDecl.cpp | 6 +++++- test/Parse/diagnostic_missing_func_keyword.swift | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 03748a3fb9de4..5098306d5ba9c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3901,8 +3901,12 @@ Parser::parseDecl(ParseDeclOptions Flags, // diagnostic first before parseExpr moves the cursor diagnose(Tok, diag::expected_decl); // parse the rest of the expression to get past the problem. + // return it so that parsing continues at the end of the expr. parseExpr(diag::expected_expr); - // we did not parse a Decl here so we need to report an error + // note that makeParserError will cause the parser to + // skip to the next right brace. So anything after this will + // not be parsed at all. It does not seem to be possible to + // recover even if the code after the expr is valid. return makeParserError(); } diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 4d67d6f76e03d..bc0aa1f0bd898 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -44,7 +44,7 @@ struct Bar { a.foo = 345 // expected-error {{expected declaration}} // ensure that the id.id pattern generating an expected declaration // diagnostic does not block further diagnostics. - fisr.bar = 345 // expected-error {{expected declaration}} + fisr.bar = 345 } From 8a56bb80c477de49138dfee5aa80ffb92c263b3b Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Sat, 25 Jul 2020 21:16:53 -0600 Subject: [PATCH 08/10] First pass at testing for probable functions. --- lib/Parse/ParseDecl.cpp | 106 +++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5098306d5ba9c..b250f2890395f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3890,26 +3890,6 @@ Parser::parseDecl(ParseDeclOptions Flags, if (Flags.contains(PD_HasContainerType) && IsAtStartOfLineOrPreviousHadSemi) { - // Fail fast if we have something that starts with an identifier - // and a period, but cannot be a var or func declaration. - // we try to parse from here as an expression to pass over - // the tokens and reach a safe place to keep parsing. - const bool IdentifierPeriod = Tok.isIdentifierOrUnderscore() && - peekToken().isAny(tok::period); - - if (IdentifierPeriod) { - // diagnostic first before parseExpr moves the cursor - diagnose(Tok, diag::expected_decl); - // parse the rest of the expression to get past the problem. - // return it so that parsing continues at the end of the expr. - parseExpr(diag::expected_expr); - // note that makeParserError will cause the parser to - // skip to the next right brace. So anything after this will - // not be parsed at all. It does not seem to be possible to - // recover even if the code after the expr is valid. - return makeParserError(); - } - // Emit diagnostics if we meet an identifier/operator where a declaration // is expected, perhaps the user forgot the 'func' or 'var' keyword. // @@ -3946,8 +3926,45 @@ Parser::parseDecl(ParseDeclOptions Flags, break; } - const bool IsProbablyFuncDecl = - Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator(); + bool IsProbablyFuncDecl = false; + + if (Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator()) { + BacktrackingScope backtrackingScope(*this); + + // need to see generic-params?, then signature after func ID + // + + // nb need to handle operator with < at end, splitting off + // the < to be generic params + +// if this is an operator with a left-angle at the end, +// split the token into operator and generic. + + Identifier SimpleName; + SourceLoc NameLoc; + handleOperatorWithGeneric(SimpleName, NameLoc); + + consumeToken(); // eat up the identifier + + auto GenericParamResult = maybeParseGenericParams(); + auto GenericParams = GenericParamResult.getPtrOrNull(); + + DeclName FullName; + ParameterList *BodyParams; + DefaultArgumentInfo DefaultArgs; + SourceLoc throwsLoc; + bool rethrows; + TypeRepr *FuncRetTy = nullptr; + + auto parsedSignatureOK = parseFunctionSignature(SimpleName, + FullName, BodyParams, DefaultArgs, + throwsLoc, rethrows, FuncRetTy); + if (parsedSignatureOK.isSuccess()) { + IsProbablyFuncDecl = true; + } + // backtrackingScope goes out of scope, returns to identifier + + } if (IsProbablyFuncDecl) { @@ -6274,6 +6291,24 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, } } + +void Parser::handleOperatorWithGeneric(Identifier & SimpleName, + SourceLoc &NameLoc) { + if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { + // If the name is an operator token that ends in '<' and the following token + // is an identifier, split the '<' off as a separate token. This allows + // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type + // variable '' as expected. + auto NameStr = Tok.getText(); + if (NameStr.size() > 1 && NameStr.back() == '<' && + peekToken().is(tok::identifier)) { + NameStr = NameStr.slice(0, NameStr.size() - 1); + } + SimpleName = Context.getIdentifier(NameStr); + NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, + NameStr.size()); +} + /// Parse a 'func' declaration, returning null on error. The caller /// handles this case and does recovery as appropriate. /// @@ -6318,19 +6353,20 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, // Parse function name. Identifier SimpleName; SourceLoc NameLoc; - if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { - // If the name is an operator token that ends in '<' and the following token - // is an identifier, split the '<' off as a separate token. This allows - // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type - // variable '' as expected. - auto NameStr = Tok.getText(); - if (NameStr.size() > 1 && NameStr.back() == '<' && - peekToken().is(tok::identifier)) { - NameStr = NameStr.slice(0, NameStr.size() - 1); - } - SimpleName = Context.getIdentifier(NameStr); - NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, - NameStr.size()); + handleOperatorWithGeneric(SimpleName, NameLoc); +// if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { +// // If the name is an operator token that ends in '<' and the following token +// // is an identifier, split the '<' off as a separate token. This allows +// // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type +// // variable '' as expected. +// auto NameStr = Tok.getText(); +// if (NameStr.size() > 1 && NameStr.back() == '<' && +// peekToken().is(tok::identifier)) { +// NameStr = NameStr.slice(0, NameStr.size() - 1); +// } +// SimpleName = Context.getIdentifier(NameStr); +// NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, +// NameStr.size()); // Within a protocol, recover from a missing 'static'. if (Flags & PD_InProtocol) { switch (StaticSpelling) { From 1be6f2c0659a2e9784cf9612a5d74e17b7f28364 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Wed, 5 Aug 2020 21:52:43 -0600 Subject: [PATCH 09/10] Converted the test for a property reference at type-level to a probe using parseDeclFunc and a check that the left and right parens are separated. Several tests do not pass right now. --- lib/Parse/ParseDecl.cpp | 112 ++++++++---------- .../Parse/diagnostic_call_at_type_level.swift | 12 ++ .../diagnostic_missing_func_keyword.swift | 27 ++--- 3 files changed, 69 insertions(+), 82 deletions(-) create mode 100644 test/Parse/diagnostic_call_at_type_level.swift diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b250f2890395f..cf329a4ae657e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -25,6 +25,7 @@ #include "swift/AST/LazyResolver.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/DiagnosticsParse.h" +#include "swift/AST/DiagnosticSuppression.h" #include "swift/AST/Initializer.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" @@ -3929,43 +3930,43 @@ Parser::parseDecl(ParseDeclOptions Flags, bool IsProbablyFuncDecl = false; if (Tok.isIdentifierOrUnderscore() || Tok.isAnyOperator()) { - BacktrackingScope backtrackingScope(*this); - - // need to see generic-params?, then signature after func ID - // - - // nb need to handle operator with < at end, splitting off - // the < to be generic params - -// if this is an operator with a left-angle at the end, -// split the token into operator and generic. - - Identifier SimpleName; - SourceLoc NameLoc; - handleOperatorWithGeneric(SimpleName, NameLoc); - consumeToken(); // eat up the identifier - - auto GenericParamResult = maybeParseGenericParams(); - auto GenericParams = GenericParamResult.getPtrOrNull(); - - DeclName FullName; - ParameterList *BodyParams; - DefaultArgumentInfo DefaultArgs; - SourceLoc throwsLoc; - bool rethrows; - TypeRepr *FuncRetTy = nullptr; - - auto parsedSignatureOK = parseFunctionSignature(SimpleName, - FullName, BodyParams, DefaultArgs, - throwsLoc, rethrows, FuncRetTy); - if (parsedSignatureOK.isSuccess()) { - IsProbablyFuncDecl = true; + // This is a probe, so backtrack when we're done + // and don't emit diagnostics. + BacktrackingScope backtrackingScope(*this); + DiagnosticSuppression diagnosticSuppression(Diags); + + // TODO make sure we can't get these from the environment + SourceLoc staticLoc; + StaticSpellingKind staticSpelling = StaticSpellingKind::None; + DeclAttributes attributes; + bool hasFuncKeyword = false; + + ParserResult parserResult = + parseDeclFunc(staticLoc, + staticSpelling, + Flags, + attributes, + hasFuncKeyword); + + // it looks like we always get a good parse result. + // But if there are no parentheses + // the left and right parens will be at the beginning of + // the expression and will be equal so we use + // this to reject degenerate declarations. + + if (parserResult.isNonNull()) { + auto parameterList = parserResult.get()->getParameters(); + if (parameterList->getLParenLoc() != parameterList->getRParenLoc()) { + IsProbablyFuncDecl = true; + } } - // backtrackingScope goes out of scope, returns to identifier - + // backtrackingScope and diagnostic suppression are both RAII + // when they go out of scope, Tok returns to the beginning of + // the identifier, and diagnostics are unsuppressed. } + // if probe succeeded, then do it for real if (IsProbablyFuncDecl) { DescriptiveDeclKind DescriptiveKind; @@ -6291,24 +6292,6 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD, } } - -void Parser::handleOperatorWithGeneric(Identifier & SimpleName, - SourceLoc &NameLoc) { - if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { - // If the name is an operator token that ends in '<' and the following token - // is an identifier, split the '<' off as a separate token. This allows - // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type - // variable '' as expected. - auto NameStr = Tok.getText(); - if (NameStr.size() > 1 && NameStr.back() == '<' && - peekToken().is(tok::identifier)) { - NameStr = NameStr.slice(0, NameStr.size() - 1); - } - SimpleName = Context.getIdentifier(NameStr); - NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, - NameStr.size()); -} - /// Parse a 'func' declaration, returning null on error. The caller /// handles this case and does recovery as appropriate. /// @@ -6353,20 +6336,19 @@ ParserResult Parser::parseDeclFunc(SourceLoc StaticLoc, // Parse function name. Identifier SimpleName; SourceLoc NameLoc; - handleOperatorWithGeneric(SimpleName, NameLoc); -// if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { -// // If the name is an operator token that ends in '<' and the following token -// // is an identifier, split the '<' off as a separate token. This allows -// // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type -// // variable '' as expected. -// auto NameStr = Tok.getText(); -// if (NameStr.size() > 1 && NameStr.back() == '<' && -// peekToken().is(tok::identifier)) { -// NameStr = NameStr.slice(0, NameStr.size() - 1); -// } -// SimpleName = Context.getIdentifier(NameStr); -// NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, -// NameStr.size()); + if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) { + // If the name is an operator token that ends in '<' and the following token + // is an identifier, split the '<' off as a separate token. This allows + // things like 'func ==(x:T, y:T) {}' to parse as '==' with generic type + // variable '' as expected. + auto NameStr = Tok.getText(); + if (NameStr.size() > 1 && NameStr.back() == '<' && + peekToken().is(tok::identifier)) { + NameStr = NameStr.slice(0, NameStr.size() - 1); + } + SimpleName = Context.getIdentifier(NameStr); + NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced, + NameStr.size()); // Within a protocol, recover from a missing 'static'. if (Flags & PD_InProtocol) { switch (StaticSpelling) { diff --git a/test/Parse/diagnostic_call_at_type_level.swift b/test/Parse/diagnostic_call_at_type_level.swift new file mode 100644 index 0000000000000..d60a08ca24bc4 --- /dev/null +++ b/test/Parse/diagnostic_call_at_type_level.swift @@ -0,0 +1,12 @@ +// RUN: %target-typecheck-verify-swift + +struct Bar { // expected-note {{in declaration of 'Bar'}} + var fisr = 0x5F3759DF + var a: Int + + // ensure that the id.id pattern is not interpreted as a function + a.foo = 345 // expected-error {{expected declaration}} + // ensure that the id.id pattern generating an expected declaration + // diagnostic does not block further diagnostics. + fisr.bar = 345 +} diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index bc0aa1f0bd898..60376f5f83f5a 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -25,27 +25,20 @@ protocol Brew { // expected-note {{in declaration of 'Brew'}} infix operator %% struct Bar { - fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} - // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} - // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} - lhs + lhs + rhs + rhs - } + %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} + // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} + // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} + lhs + lhs + rhs + rhs + } - _: Int = 42 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - // expected-error @-1 {{property declaration does not bind any variables}} + _: Int = 42 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + // expected-error @-1 {{property declaration does not bind any variables}} - (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - - a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - - // ensure that the id.id pattern is not interpreted as a function - a.foo = 345 // expected-error {{expected declaration}} - // ensure that the id.id pattern generating an expected declaration - // diagnostic does not block further diagnostics. - fisr.bar = 345 + (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} } class Baz { From 100c8fe0ec319aab6cb825f61f68c353abe283e0 Mon Sep 17 00:00:00 2001 From: Daniel Sweeney Date: Sun, 6 Sep 2020 16:18:23 -0600 Subject: [PATCH 10/10] Fixed a column error in `test/Parse/diagnostic_missing_func_keyword.swift` and removed resolved diagnostics from `test/Parse/line-directive.swift`. --- .../diagnostic_missing_func_keyword.swift | 18 +++++++++--------- test/Parse/line-directive.swift | 7 ------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/test/Parse/diagnostic_missing_func_keyword.swift b/test/Parse/diagnostic_missing_func_keyword.swift index 60376f5f83f5a..7c9ac009780ba 100644 --- a/test/Parse/diagnostic_missing_func_keyword.swift +++ b/test/Parse/diagnostic_missing_func_keyword.swift @@ -25,20 +25,20 @@ protocol Brew { // expected-note {{in declaration of 'Brew'}} infix operator %% struct Bar { - fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + fisr = 0x5F3759DF // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} - // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} - // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} - lhs + lhs + rhs + rhs - } + %% (lhs: T, rhs: T) -> T { // expected-error {{expected 'func' keyword in operator function declaration}} {{3-3=func }} + // expected-error @-1 {{operator '%%' declared in type 'Bar' must be 'static'}} + // expected-error @-2 {{member operator '%%' must have at least one argument of type 'Bar'}} + lhs + lhs + rhs + rhs + } - _: Int = 42 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + _: Int = 42 // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} // expected-error @-1 {{property declaration does not bind any variables}} - (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + (light, dark) = (100, 200)// expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} - a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} + a, b: Int // expected-error {{expected 'var' keyword in property declaration}} {{3-3=var }} } class Baz { diff --git a/test/Parse/line-directive.swift b/test/Parse/line-directive.swift index 45bd84afaf947..30fe7b3c9c215 100644 --- a/test/Parse/line-directive.swift +++ b/test/Parse/line-directive.swift @@ -24,13 +24,6 @@ x x // expected-error{{consecutive statements}} {{2-2=;}} // rdar://19582475 public struct S { // expected-note{{in declaration of 'S'}} -// expected-error@+8{{expected 'func' keyword in operator function declaration}} -// expected-error@+7{{operator '/' declared in type 'S' must be 'static'}} -// expected-error@+6{{expected '(' in argument list of function declaration}} -// expected-error@+5{{operators must have one or two arguments}} -// expected-error@+4{{member operator '/()' must have at least one argument of type 'S'}} -// expected-error@+3{{expected '{' in body of function declaration}} -// expected-error@+2{{consecutive declarations on a line must be separated by ';}} // expected-error@+1{{expected declaration}} / ###line 25 "line-directive.swift" }