From cfea6f4f93384e5f203ca41395480364e1d73ead Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 15:25:44 -0700 Subject: [PATCH 1/9] removing data and item from reserved words --- src/QsCompiler/DataStructures/ReservedKeywords.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 408a9d6a8a..3d5b98b11c 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -202,9 +202,6 @@ module InternalUse = "Borrow" "Return" - "Data" - "Item" - "QVoid" "Int64" "BigInteger" From c9ee1bd6c2b6379feefdbb3e1d08dfbf8db1edbd Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 17:18:33 -0700 Subject: [PATCH 2/9] giving warnings for reserved underscore patterns --- src/QsCompiler/DataStructures/Diagnostics.fs | 10 ++++-- .../DataStructures/ReservedKeywords.fs | 3 ++ src/QsCompiler/Tests.Compiler/SyntaxTests.fs | 35 ++++++++++++++++++- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 19 +++++++--- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 4fded15421..e4610301c0 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -47,8 +47,9 @@ type ErrorCode = | InvalidConstructorExpression = 3030 | InvalidKeywordWithinExpression = 3032 | InvalidUseOfReservedKeyword = 3033 - | ExcessContinuation = 3034 - | NonCallExprAsStatement = 3035 + | InvalidUseOfUnderscorePattern = 3034 + | ExcessContinuation = 3035 + | NonCallExprAsStatement = 3036 | InvalidExpression = 3101 | MissingExpression = 3102 @@ -320,6 +321,8 @@ type WarningCode = | DeprecatedNOToperator = 3301 | DeprecatedANDoperator = 3302 | DeprecatedORoperator = 3303 + | UseOfFutureReservedKeyword = 3304 + | UseOfUnderscorePattern = 3305 | DeprecatedRUSloopInFunction = 4001 | DiscardingItemInAssignment = 5001 @@ -428,6 +431,7 @@ type DiagnosticItem = | ErrorCode.InvalidConstructorExpression -> "Syntax error in constructor expression." | ErrorCode.InvalidKeywordWithinExpression -> "Invalid use of a reserved keyword within an expression." | ErrorCode.InvalidUseOfReservedKeyword -> "The symbol is reserved for internal use only." + | ErrorCode.InvalidUseOfUnderscorePattern -> "The use of double underscores, and underscores before and after dots is reserved for internal use only." | ErrorCode.ExcessContinuation -> "Unexpected code fragment." | ErrorCode.NonCallExprAsStatement -> "An expression used as a statement must be a call expression." @@ -712,6 +716,8 @@ type DiagnosticItem = | WarningCode.DeprecatedNOToperator -> "Deprecated syntax. Use \"not\" to denote the logical NOT operator." | WarningCode.DeprecatedANDoperator -> "Deprecated syntax. Use \"and\" to denote the logical AND operator." | WarningCode.DeprecatedORoperator -> "Deprecated syntax. Use \"or\" to denote the logical OR operator." + | WarningCode.UseOfFutureReservedKeyword -> "The symbol will be reserved for internal use in the future." + | WarningCode.UseOfUnderscorePattern -> "The use of double underscores, and underscores before and after dots will be reserved for internal use in the future." | WarningCode.DeprecatedRUSloopInFunction -> "The use of repeat-until-success-loops within functions may not be supported in the future. Please use a while-loop instead." | WarningCode.DiscardingItemInAssignment -> "The expression on the right hand side is discarded on assignment and can be omitted." diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 3d5b98b11c..408a9d6a8a 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -202,6 +202,9 @@ module InternalUse = "Borrow" "Return" + "Data" + "Item" + "QVoid" "Int64" "BigInteger" diff --git a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs index 66a64c89e6..64dcabb375 100644 --- a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs +++ b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs @@ -17,7 +17,40 @@ open Xunit let private rawString = getStringContent (manyChars anyChar) |>> fst -// Component parsers + +[] +let ``Reserved patterns`` () = + [ + ("_mySymbol" , true , Some "_mySymbol" , []) + ("mySymbol_" , true , Some "mySymbol_" , []) + ("my_symbol" , true , Some "my_symbol" , []) + ("my__symbol", true , Some "my__symbol", [Warning WarningCode.UseOfUnderscorePattern]) + ("__mySymbol", true , Some "__mySymbol", [Warning WarningCode.UseOfUnderscorePattern]) + ("mySymbol__", true , Some "mySymbol__", [Warning WarningCode.UseOfUnderscorePattern]) + ("__my__sym" , true , Some "__my__sym" , [Warning WarningCode.UseOfUnderscorePattern]) + ("my__sym__" , true , Some "my__sym__" , [Warning WarningCode.UseOfUnderscorePattern]) + ("__mysym__" , true , None , [Error ErrorCode.InvalidUseOfReservedKeyword]) + ] + |> List.iter (testOne (symbolNameLike ErrorCode.InvalidIdentifierName)) + [ + ("a.b" , true , ([Some "a" ],Some "b"), []) + ("_a.b" , true , ([Some "_a" ],Some "b"), []) + ("a_.b" , true , ([Some "a_" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a._b" , true , ([Some "a" ],Some "_b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b_" , true , ([Some "a" ],Some "b_"), []) + ("_a.b_" , true , ([Some "_a" ],Some "b_"), []) + ("a_._b" , true , ([Some "a_" ],Some "_b"), [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b" , true , ([Some "__a" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a__a.b" , true , ([Some "a__a" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a__.b" , true , ([Some "a__" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.__b" , true , ([Some "a" ],Some "__b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__b" , true , ([Some "a" ],Some "b__b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__" , true , ([Some "a" ],Some "b__"), [Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b__" , true , ([Some "__a" ],Some "b__"), [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) + ] + |> List.iter (testOne (multiSegmentSymbol ErrorCode.InvalidIdentifierName |>> fst)) + + [] let ``String parser tests`` () = [ diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index e45422090b..855cd72763 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -345,17 +345,26 @@ let internal symbolNameLike errCode = preCheckStart = isSymbolStart, preCheckContinue = isSymbolContinuation) |> identifier getPosition .>>. id .>>. getPosition |>> fun ((p1, name), p2) -> name, (p1,p2) - let whenValid (name : string, range) = - let isReserved = name.StartsWith "__" && name.EndsWith "__" || InternalUse.CsKeywords.Contains name + let whenValid ((isAfterDot, (name : string, range)), isBeforeDot) = + // TODO: + // The warning for reservedUnderscorePattern should be replace with an error in the future, + // and the first half of isReserved should be removed. + let reservedUnderscorePattern = name.Contains "__" || (isAfterDot && name.StartsWith "_") || (isBeforeDot && name.EndsWith "_") + let isReserved = name.StartsWith "__" && name.EndsWith "__" || InternalUse.CsKeywords.Contains name let isCsKeyword = SyntaxFacts.IsKeywordKind (SyntaxFacts.GetKeywordKind name) let moreThanUnderscores = name.TrimStart('_').Length <> 0 if isCsKeyword || isReserved then buildError (preturn range) ErrorCode.InvalidUseOfReservedKeyword >>% None - else if moreThanUnderscores then preturn name |>> Some - else buildError (preturn range) errCode >>% None + elif not moreThanUnderscores then buildError (preturn range) errCode >>% None + elif reservedUnderscorePattern then buildWarning (preturn range) WarningCode.UseOfUnderscorePattern >>% Some name + else preturn name |>> Some let invalid = let invalidName = pchar '\'' |> opt >>. manySatisfy isDigit >>. identifier buildError (getPosition .>> invalidName .>>. getPosition) errCode >>% None - notFollowedBy qsReservedKeyword >>. attempt (identifier >>= whenValid <|> invalid) // NOTE: *needs* to fail on reserverd keywords here! + let validSymbolName = + let checkPrecededByDot = previousCharSatisfies ((=)'.') >>% true <|>% false + let checkFollowedByDot = nextCharSatisfies ((=)'.') >>% true <|>% false + (checkPrecededByDot .>>. identifier .>>. checkFollowedByDot) >>= whenValid + notFollowedBy qsReservedKeyword >>. attempt (validSymbolName <|> invalid) // NOTE: *needs* to fail on reserverd keywords here! /// Handles permissive parsing of a symbol: /// Uses symbolNameLike to generate suitable errors if the current symbol-like text is not a valid symbol in Q# From 2d38fcf8e3e1196cc738fbf7e13b0f710fc4a568 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 17:21:42 -0700 Subject: [PATCH 3/9] instead of TODO use a REL0920 tag for what should be done next month. --- src/QsCompiler/DataStructures/ReservedKeywords.fs | 4 ++-- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 408a9d6a8a..7e50c2fb35 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -202,8 +202,8 @@ module InternalUse = "Borrow" "Return" - "Data" - "Item" + "Data" // REL0920: can be removed once we give errors for underscore patterns + "Item" // REL0920: can be removed once we give errors for underscore patterns "QVoid" "Int64" diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index 855cd72763..093743f388 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -346,7 +346,7 @@ let internal symbolNameLike errCode = preCheckContinue = isSymbolContinuation) |> identifier getPosition .>>. id .>>. getPosition |>> fun ((p1, name), p2) -> name, (p1,p2) let whenValid ((isAfterDot, (name : string, range)), isBeforeDot) = - // TODO: + // REL0920: // The warning for reservedUnderscorePattern should be replace with an error in the future, // and the first half of isReserved should be removed. let reservedUnderscorePattern = name.Contains "__" || (isAfterDot && name.StartsWith "_") || (isBeforeDot && name.EndsWith "_") From 3e76c992c147522ff86873feeb3406790537f855 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 17:41:57 -0700 Subject: [PATCH 4/9] forgot to update another test --- src/QsCompiler/Tests.Compiler/SyntaxTests.fs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs index 64dcabb375..5157c5cb94 100644 --- a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs +++ b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs @@ -174,7 +174,6 @@ let ``Symbol name tests`` () = ("_a", true, "_a", []); ("_", false, "", []); ("__", false, "", []); - ("__a", true, "__a", []); ("функция25", true, "функция25", []); // Russian word 'function' followed by '25' ("λ", true, "λ", []); // Greek small letter Lambda ("ℵ", true, "ℵ", []); // Hebrew capital letter Aleph From 1ee659124290f7d0de2e554c048f6386b52e47da Mon Sep 17 00:00:00 2001 From: bettinaheim <34236215+bettinaheim@users.noreply.github.com> Date: Wed, 19 Aug 2020 17:52:15 -0700 Subject: [PATCH 5/9] Update src/QsCompiler/TextProcessor/SyntaxBuilder.fs Co-authored-by: Chris Granade --- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index 093743f388..30487b125c 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -347,7 +347,7 @@ let internal symbolNameLike errCode = getPosition .>>. id .>>. getPosition |>> fun ((p1, name), p2) -> name, (p1,p2) let whenValid ((isAfterDot, (name : string, range)), isBeforeDot) = // REL0920: - // The warning for reservedUnderscorePattern should be replace with an error in the future, + // The warning for reservedUnderscorePattern should be replaced with an error in the future, // and the first half of isReserved should be removed. let reservedUnderscorePattern = name.Contains "__" || (isAfterDot && name.StartsWith "_") || (isBeforeDot && name.EndsWith "_") let isReserved = name.StartsWith "__" && name.EndsWith "__" || InternalUse.CsKeywords.Contains name From fadc709dce63176af7134defdf3421075f010be8 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 19:09:21 -0700 Subject: [PATCH 6/9] prevent underscores at the end of a namespace name and allow ._ --- src/QsCompiler/DataStructures/Diagnostics.fs | 4 +- src/QsCompiler/Tests.Compiler/SyntaxTests.fs | 46 +++++++++++++------ .../TextProcessor/QsFragmentParsing.fs | 22 +++++++-- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 15 ++---- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index e4610301c0..96965d1206 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -431,7 +431,7 @@ type DiagnosticItem = | ErrorCode.InvalidConstructorExpression -> "Syntax error in constructor expression." | ErrorCode.InvalidKeywordWithinExpression -> "Invalid use of a reserved keyword within an expression." | ErrorCode.InvalidUseOfReservedKeyword -> "The symbol is reserved for internal use only." - | ErrorCode.InvalidUseOfUnderscorePattern -> "The use of double underscores, and underscores before and after dots is reserved for internal use only." + | ErrorCode.InvalidUseOfUnderscorePattern -> "The use of double underscores, and underscores before a dot or at the end of a namespace name, is reserved for internal use only." | ErrorCode.ExcessContinuation -> "Unexpected code fragment." | ErrorCode.NonCallExprAsStatement -> "An expression used as a statement must be a call expression." @@ -717,7 +717,7 @@ type DiagnosticItem = | WarningCode.DeprecatedANDoperator -> "Deprecated syntax. Use \"and\" to denote the logical AND operator." | WarningCode.DeprecatedORoperator -> "Deprecated syntax. Use \"or\" to denote the logical OR operator." | WarningCode.UseOfFutureReservedKeyword -> "The symbol will be reserved for internal use in the future." - | WarningCode.UseOfUnderscorePattern -> "The use of double underscores, and underscores before and after dots will be reserved for internal use in the future." + | WarningCode.UseOfUnderscorePattern -> "The use of double underscores, and underscores before a dot or at the end of a namespace name, will be reserved for internal use in the future." | WarningCode.DeprecatedRUSloopInFunction -> "The use of repeat-until-success-loops within functions may not be supported in the future. Please use a while-loop instead." | WarningCode.DiscardingItemInAssignment -> "The expression on the right hand side is discarded on assignment and can be omitted." diff --git a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs index 5157c5cb94..4846b284c9 100644 --- a/src/QsCompiler/Tests.Compiler/SyntaxTests.fs +++ b/src/QsCompiler/Tests.Compiler/SyntaxTests.fs @@ -8,6 +8,7 @@ open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.TextProcessing.ExpressionParsing +open Microsoft.Quantum.QsCompiler.TextProcessing.CodeFragments open Microsoft.Quantum.QsCompiler.TextProcessing.SyntaxBuilder open System open System.Collections.Immutable @@ -33,22 +34,39 @@ let ``Reserved patterns`` () = ] |> List.iter (testOne (symbolNameLike ErrorCode.InvalidIdentifierName)) [ - ("a.b" , true , ([Some "a" ],Some "b"), []) - ("_a.b" , true , ([Some "_a" ],Some "b"), []) - ("a_.b" , true , ([Some "a_" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a._b" , true , ([Some "a" ],Some "_b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a.b_" , true , ([Some "a" ],Some "b_"), []) - ("_a.b_" , true , ([Some "_a" ],Some "b_"), []) - ("a_._b" , true , ([Some "a_" ],Some "_b"), [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) - ("__a.b" , true , ([Some "__a" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a__a.b" , true , ([Some "a__a" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a__.b" , true , ([Some "a__" ],Some "b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a.__b" , true , ([Some "a" ],Some "__b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a.b__b" , true , ([Some "a" ],Some "b__b"), [Warning WarningCode.UseOfUnderscorePattern]) - ("a.b__" , true , ([Some "a" ],Some "b__"), [Warning WarningCode.UseOfUnderscorePattern]) - ("__a.b__" , true , ([Some "__a" ],Some "b__"), [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) + ("a.b" , true , ([Some "a" ], Some "b" ), []) + ("_a.b" , true , ([Some "_a" ], Some "b" ), []) + ("a_.b" , true , ([Some "a_" ], Some "b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("a._b" , true , ([Some "a" ], Some "_b" ), []) + ("a.b_" , true , ([Some "a" ], Some "b_" ), []) + ("_a.b_" , true , ([Some "_a" ], Some "b_" ), []) + ("a_._b" , true , ([Some "a_" ], Some "_b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b" , true , ([Some "__a" ], Some "b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("a__a.b" , true , ([Some "a__a"], Some "b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("a__.b" , true , ([Some "a__" ], Some "b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.__b" , true , ([Some "a" ], Some "__b" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__b" , true , ([Some "a" ], Some "b__b"), [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__" , true , ([Some "a" ], Some "b__" ), [Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b__", true , ([Some "__a" ], Some "b__" ), [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) ] |> List.iter (testOne (multiSegmentSymbol ErrorCode.InvalidIdentifierName |>> fst)) + [ + ("a.b" , true , Some "a.b" , []) + ("_a.b" , true , Some "_a.b" , []) + ("a_.b" , true , Some "a_.b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a._b" , true , Some "a._b" , []) + ("a.b_" , true , Some "a.b_" , [Warning WarningCode.UseOfUnderscorePattern]) + ("_a.b_" , true , Some "_a.b_" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a_._b" , true , Some "a_._b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b" , true , Some "__a.b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a__a.b" , true , Some "a__a.b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a__.b" , true , Some "a__.b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a.__b" , true , Some "a.__b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__b" , true , Some "a.b__b" , [Warning WarningCode.UseOfUnderscorePattern]) + ("a.b__" , true , Some "a.b__" , [Warning WarningCode.UseOfUnderscorePattern]) + ("__a.b__", true , Some "__a.b__", [Warning WarningCode.UseOfUnderscorePattern; Warning WarningCode.UseOfUnderscorePattern]) + ] + |> List.iter (testOne (namespaceName |>> fst)) [] diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index 80aa2b276e..b1a8906a80 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -59,15 +59,31 @@ let private buildSymbolTuple (items, range : Position * Position) = let private expectedIdentifierDeclaration continuation = expected localIdentifier ErrorCode.InvalidIdentifierDeclaration ErrorCode.MissingIdentifierDeclaration invalidSymbol continuation +/// Uses multiSegmentSymbol to parse the name of a namespace, +/// concatenates all path segments and the symbol with a dot, and returns a the concatenated string as Some. +/// Returns None if the path contains segments that are None (i.e. invalid). +/// Generates a suitable diagnostic if the namespace name ends in an underscore. +let internal namespaceName = // internal for testing purposes + let asNamespaceName ((path, sym : string option), range : Position * Position) = + let names = [for segment in path do yield segment ] @ [sym] + let namespaceStr = names |> List.choose id |> String.concat "." + if names |> List.contains None then (None, range) |> preturn + elif sym.Value.EndsWith "_" && not (namespaceStr.Contains "__" || namespaceStr.Contains "_.") then // REL0920: remove the second half and return None for pattern errors + buildWarning (preturn range) WarningCode.UseOfUnderscorePattern >>% (Some namespaceStr, range) + else (Some namespaceStr, range) |> preturn + multiSegmentSymbol ErrorCode.InvalidPathSegment >>= asNamespaceName + /// Given a continuation (parser), attempts to parse a qualified QsSymbol /// using multiSegmentSymbol to generate suitable errors for invalid symbol and/or path names, /// and returns the parsed symbol, or a QsSymbol representing an invalid symbol (parsing failure) if the parsing fails. /// On failure, either raises an MissingQualifiedSymbol if the given continuation succeeds at the current position, /// or raises an InvalidQualifiedSymbol and advances until the given continuation succeeds otherwise. /// Does not apply the given continuation. -let private expectedNamespaceName continuation = - let path = multiSegmentSymbol ErrorCode.InvalidPathSegment |>> asSymbol - expected path ErrorCode.InvalidQualifiedSymbol ErrorCode.MissingQualifiedSymbol invalidSymbol continuation +let private expectedNamespaceName continuation = + let namespaceName = namespaceName |>> function + | None, _ -> (InvalidSymbol, Null) |> QsSymbol.New + | Some name, range -> (name |> NonNullable.New |> Symbol, range) |> QsSymbol.New + expected namespaceName ErrorCode.InvalidQualifiedSymbol ErrorCode.MissingQualifiedSymbol invalidSymbol continuation /// Parses the condition e.g. for if, elif and until clauses. /// Uses optTupleBrackets to raise the corresponding missing bracket errors if the condition is not within tuple brackets. diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index 093743f388..6dbc6a2a21 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -345,11 +345,11 @@ let internal symbolNameLike errCode = preCheckStart = isSymbolStart, preCheckContinue = isSymbolContinuation) |> identifier getPosition .>>. id .>>. getPosition |>> fun ((p1, name), p2) -> name, (p1,p2) - let whenValid ((isAfterDot, (name : string, range)), isBeforeDot) = + let whenValid ((name : string, range), isBeforeDot) = // REL0920: // The warning for reservedUnderscorePattern should be replace with an error in the future, // and the first half of isReserved should be removed. - let reservedUnderscorePattern = name.Contains "__" || (isAfterDot && name.StartsWith "_") || (isBeforeDot && name.EndsWith "_") + let reservedUnderscorePattern = name.Contains "__" || (isBeforeDot && name.EndsWith "_") let isReserved = name.StartsWith "__" && name.EndsWith "__" || InternalUse.CsKeywords.Contains name let isCsKeyword = SyntaxFacts.IsKeywordKind (SyntaxFacts.GetKeywordKind name) let moreThanUnderscores = name.TrimStart('_').Length <> 0 @@ -361,9 +361,8 @@ let internal symbolNameLike errCode = let invalidName = pchar '\'' |> opt >>. manySatisfy isDigit >>. identifier buildError (getPosition .>> invalidName .>>. getPosition) errCode >>% None let validSymbolName = - let checkPrecededByDot = previousCharSatisfies ((=)'.') >>% true <|>% false let checkFollowedByDot = nextCharSatisfies ((=)'.') >>% true <|>% false - (checkPrecededByDot .>>. identifier .>>. checkFollowedByDot) >>= whenValid + (identifier .>>. checkFollowedByDot) >>= whenValid notFollowedBy qsReservedKeyword >>. attempt (validSymbolName <|> invalid) // NOTE: *needs* to fail on reserverd keywords here! /// Handles permissive parsing of a symbol: @@ -388,14 +387,6 @@ let internal asQualifiedSymbol ((path, sym), range : Position * Position) = let (ns, sym) = (String.concat "." parts.[0..parts.Length-2]) |> NonNullable.New, parts.[parts.Length-1] |> NonNullable.New (QualifiedSymbol (ns, sym), range) |> QsSymbol.New -/// Given the path, the symbol and the range parsed by multiSegmentSymbol, -/// concatenates all path segments and the symbol with a dot, and returns a simple Symbol of the concatenated string as QsSymbol. -/// Returns a QsSymbol corresponding to an invalid symbol if the path contains segments that are None (i.e. invalid). -let internal asSymbol ((path, sym), range : Position * Position) = - let names = [for segment in path do yield segment ] @ [sym] - if names |> List.contains None then (InvalidSymbol, Null) |> QsSymbol.New - else (names |> List.choose id |> String.concat "." |> NonNullable.New |> Symbol, range) |> QsSymbol.New - /// Handles permissive parsing of a qualified symbol: /// Uses symbolNameLike for each path fragment separated by dots /// to generate suitable errors if the symbol-like fragment text is not a valid symbol in Q#. From a365f1b6ff95cb0a5b033b31334c30781c746254 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Aug 2020 19:18:05 -0700 Subject: [PATCH 7/9] diagnostic message --- src/QsCompiler/DataStructures/Diagnostics.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/DataStructures/Diagnostics.fs b/src/QsCompiler/DataStructures/Diagnostics.fs index 96965d1206..86437c1ccc 100644 --- a/src/QsCompiler/DataStructures/Diagnostics.fs +++ b/src/QsCompiler/DataStructures/Diagnostics.fs @@ -431,7 +431,7 @@ type DiagnosticItem = | ErrorCode.InvalidConstructorExpression -> "Syntax error in constructor expression." | ErrorCode.InvalidKeywordWithinExpression -> "Invalid use of a reserved keyword within an expression." | ErrorCode.InvalidUseOfReservedKeyword -> "The symbol is reserved for internal use only." - | ErrorCode.InvalidUseOfUnderscorePattern -> "The use of double underscores, and underscores before a dot or at the end of a namespace name, is reserved for internal use only." + | ErrorCode.InvalidUseOfUnderscorePattern -> "Double underscores as well as underscores before a dot or at the end of a namespace name are reserved for internal use only." | ErrorCode.ExcessContinuation -> "Unexpected code fragment." | ErrorCode.NonCallExprAsStatement -> "An expression used as a statement must be a call expression." @@ -717,7 +717,7 @@ type DiagnosticItem = | WarningCode.DeprecatedANDoperator -> "Deprecated syntax. Use \"and\" to denote the logical AND operator." | WarningCode.DeprecatedORoperator -> "Deprecated syntax. Use \"or\" to denote the logical OR operator." | WarningCode.UseOfFutureReservedKeyword -> "The symbol will be reserved for internal use in the future." - | WarningCode.UseOfUnderscorePattern -> "The use of double underscores, and underscores before a dot or at the end of a namespace name, will be reserved for internal use in the future." + | WarningCode.UseOfUnderscorePattern -> "Double underscores as well as underscores before a dot or at the end of a namespace name will be reserved for internal use in the future." | WarningCode.DeprecatedRUSloopInFunction -> "The use of repeat-until-success-loops within functions may not be supported in the future. Please use a while-loop instead." | WarningCode.DiscardingItemInAssignment -> "The expression on the right hand side is discarded on assignment and can be omitted." From d40c227e23d944e2a78636e412a48daf46580bf3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 20 Aug 2020 07:27:17 -0700 Subject: [PATCH 8/9] need to update the used snippet namespace --- src/QsCompiler/CommandLineTool/Options.cs | 4 ++-- .../Tests.Compiler/CommandLineTests.fs | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/QsCompiler/CommandLineTool/Options.cs b/src/QsCompiler/CommandLineTool/Options.cs index d40aa607a1..de79138fe8 100644 --- a/src/QsCompiler/CommandLineTool/Options.cs +++ b/src/QsCompiler/CommandLineTool/Options.cs @@ -265,12 +265,12 @@ private static NonNullable SNIPPET_FILE_ID /// /// name of the namespace within which code snippets are compiled /// - private const string SNIPPET_NAMESPACE = "_CODE_SNIPPET_NS_"; + private const string SNIPPET_NAMESPACE = "CODE_SNIPPET_NS"; /// /// name of the callable within which code snippets are compiled /// - private const string SNIPPET_CALLABLE = "_CODE_SNIPPET_CALLABLE_"; + private const string SNIPPET_CALLABLE = "CODE_SNIPPET_CALLABLE"; /// /// wraps the given content into a namespace and callable that maps Unit to Unit diff --git a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs index 75563d0fa0..47961c0290 100644 --- a/src/QsCompiler/Tests.Compiler/CommandLineTests.fs +++ b/src/QsCompiler/Tests.Compiler/CommandLineTests.fs @@ -242,7 +242,7 @@ let ``generate docs`` () = let ``find path relative`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.qs") let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -252,7 +252,7 @@ let ``find path relative to outputfolder`` () = let fullPath = Path.Combine(Path.GetFullPath "alpha","beta","c","test-path.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine (pathRoot,"foo","bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (pathRoot, "foo", "bar", "alpha", "beta", "c", "test-path.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -262,7 +262,7 @@ let ``find path relative to relative outputfolder`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine("..","foo","bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (parentDir,"foo","bar","alpha","beta","c","test-path.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -272,7 +272,7 @@ let ``find path absolute`` () = let fileName = Path.Combine (pathRoot,"alpha","beta","c","test-path.qs") let fullPath = Path.GetFullPath fileName let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.GetFullPath "test-path.g.cs" let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -282,7 +282,7 @@ let ``find path absolute to outputfolder`` () = let fullPath = Path.Combine (pathRoot, "alpha","beta","c","test-path.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine (pathRoot, "foo","bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (pathRoot, "foo","bar", "test-path.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -291,7 +291,7 @@ let ``find path absolute to outputfolder`` () = let ``find path relative to here`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.qs") let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (Path.GetFullPath "alpha","beta","c","test-path.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -300,7 +300,7 @@ let ``find path relative to here`` () = let ``find path relative with spaces`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha", "some beta", "c", "test 00.qs") let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (Path.GetFullPath "alpha","some beta","c","test 00.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -310,7 +310,7 @@ let ``find path relative to outputfolder with spaces`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","some beta","c","test 00.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine (pathRoot, "foo", "some bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.GetFullPath (Path.Combine (pathRoot, "foo","some bar","alpha","some beta","c","test 00.g.cs")) let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -320,7 +320,7 @@ let ``find path relative to relative outputfolder with spaces`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","some beta","c","test 00.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine ("..","some foo","bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (parentDir,"some foo","bar","alpha","some beta","c","test 00.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -329,7 +329,7 @@ let ``find path relative to relative outputfolder with spaces`` () = let ``find path absolute with spaces`` () = let fullPath = Path.Combine (pathRoot, "alpha","some beta","c","test 02.qs") let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.GetFullPath "test 02.g.cs" let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -339,7 +339,7 @@ let ``find path absolute to outputfolder with spaces`` () = let fullPath = Path.Combine (pathRoot, "alpha", "some beta", "c", "test 02.qs") let options = new BuildCompilation.BuildOptions() options.OutputFolder <- Path.Combine(pathRoot, "foo", "some bar") - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (pathRoot, "foo", "some bar", "test 02.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) @@ -348,7 +348,7 @@ let ``find path absolute to outputfolder with spaces`` () = let ``find path relative to here with spaces`` () = let fullPath = Path.Combine (Path.GetFullPath "alpha","some beta","c","test 03.qs") let options = new BuildCompilation.BuildOptions() - let id = CompilationUnitManager.TryGetFileId (new Uri(fullPath)) |> snd + let id = CompilationUnitManager.GetFileId (new Uri(fullPath)) let expected = Path.Combine (Path.GetFullPath "alpha","some beta","c","test 03.g.cs") let actual = CompilationLoader.GeneratedFile(id, options.OutputFolder, ".g.cs") Assert.Equal(expected, actual) From 5e61d674afd814c19d51a33e1d3f857bf8f5ebe9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 20 Aug 2020 13:56:30 -0700 Subject: [PATCH 9/9] addressing review comments --- src/QsCompiler/TextProcessor/QsFragmentParsing.fs | 2 +- src/QsCompiler/TextProcessor/SyntaxBuilder.fs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs index b1a8906a80..02ec2ddcce 100644 --- a/src/QsCompiler/TextProcessor/QsFragmentParsing.fs +++ b/src/QsCompiler/TextProcessor/QsFragmentParsing.fs @@ -65,7 +65,7 @@ let private expectedIdentifierDeclaration continuation = /// Generates a suitable diagnostic if the namespace name ends in an underscore. let internal namespaceName = // internal for testing purposes let asNamespaceName ((path, sym : string option), range : Position * Position) = - let names = [for segment in path do yield segment ] @ [sym] + let names = path @ [sym] let namespaceStr = names |> List.choose id |> String.concat "." if names |> List.contains None then (None, range) |> preturn elif sym.Value.EndsWith "_" && not (namespaceStr.Contains "__" || namespaceStr.Contains "_.") then // REL0920: remove the second half and return None for pattern errors diff --git a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs index 6a615d41ec..e4d35d9c86 100644 --- a/src/QsCompiler/TextProcessor/SyntaxBuilder.fs +++ b/src/QsCompiler/TextProcessor/SyntaxBuilder.fs @@ -347,15 +347,15 @@ let internal symbolNameLike errCode = getPosition .>>. id .>>. getPosition |>> fun ((p1, name), p2) -> name, (p1,p2) let whenValid ((name : string, range), isBeforeDot) = // REL0920: - // The warning for reservedUnderscorePattern should be replaced with an error in the future, + // The warning for futureReservedUnderscorePattern should be replaced with an error in the future, // and the first half of isReserved should be removed. - let reservedUnderscorePattern = name.Contains "__" || (isBeforeDot && name.EndsWith "_") + let futureReservedUnderscorePattern = name.Contains "__" || (isBeforeDot && name.EndsWith "_") let isReserved = name.StartsWith "__" && name.EndsWith "__" || InternalUse.CsKeywords.Contains name let isCsKeyword = SyntaxFacts.IsKeywordKind (SyntaxFacts.GetKeywordKind name) let moreThanUnderscores = name.TrimStart('_').Length <> 0 if isCsKeyword || isReserved then buildError (preturn range) ErrorCode.InvalidUseOfReservedKeyword >>% None elif not moreThanUnderscores then buildError (preturn range) errCode >>% None - elif reservedUnderscorePattern then buildWarning (preturn range) WarningCode.UseOfUnderscorePattern >>% Some name + elif futureReservedUnderscorePattern then buildWarning (preturn range) WarningCode.UseOfUnderscorePattern >>% Some name else preturn name |>> Some let invalid = let invalidName = pchar '\'' |> opt >>. manySatisfy isDigit >>. identifier