Skip to content

Error on invalid declarations in type definitions. #18813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 64 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1799aaf
make attribute targets mismatches a warning and not an error.
edgarfgp Apr 23, 2025
55507e9
release notes
edgarfgp Apr 23, 2025
1738018
update tests
edgarfgp Apr 23, 2025
65f5bb6
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 24, 2025
0c97b9d
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 27, 2025
6f2b706
update baselines
edgarfgp Apr 29, 2025
e8f1bb0
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 29, 2025
75d8f5e
Update baselines
edgarfgp Apr 29, 2025
4f2e97e
Merge branch 'fix-attr-targets' of github.com:edgarfgp/fsharp into fi…
edgarfgp Apr 29, 2025
63be5d5
Merge branch 'main' into fix-attr-targets
edgarfgp Apr 30, 2025
4248f2a
Move attribute form logic to an AP
edgarfgp Apr 30, 2025
cc96217
Merge branch 'main' into fix-attr-targets
edgarfgp May 1, 2025
e270b88
Merge branch 'main' into fix-attr-targets
edgarfgp May 1, 2025
e0cc65a
Merge branch 'main' into fix-attr-targets
edgarfgp May 2, 2025
1e29d58
Merge branch 'main' into fix-attr-targets
edgarfgp May 5, 2025
1470bf9
Merge branch 'main' into fix-attr-targets
edgarfgp May 6, 2025
8988215
Merge branch 'main' into fix-attr-targets
edgarfgp May 7, 2025
74712e8
Merge branch 'main' into fix-attr-targets
edgarfgp May 12, 2025
967c4a9
Merge branch 'main' of github.com:edgarfgp/fsharp
edgarfgp May 13, 2025
a30cef4
Merge branch 'dotnet:main' into main
edgarfgp May 22, 2025
5fa0480
Merge branch 'dotnet:main' into main
edgarfgp May 24, 2025
15e3d34
Merge branch 'dotnet:main' into main
edgarfgp May 29, 2025
b7ffcf8
Merge branch 'dotnet:main' into main
edgarfgp Jun 6, 2025
5bde641
Merge branch 'dotnet:main' into main
edgarfgp Jul 26, 2025
0f7c23c
Merge branch 'dotnet:main' into main
edgarfgp Jul 29, 2025
8f0683d
Check if the module appears to be incorrectly nested based on indenta…
edgarfgp Aug 3, 2025
569b382
one more test
edgarfgp Aug 3, 2025
89ec72a
release notes
edgarfgp Aug 3, 2025
e61b027
Merge branch 'main' into fix-10066
edgarfgp Aug 3, 2025
6074eb5
Merge branch 'main' into fix-10066
edgarfgp Aug 4, 2025
765a7ff
Merge branch 'main' into fix-10066
edgarfgp Aug 6, 2025
a252caf
Move check to ParseHelpers.fs
edgarfgp Aug 6, 2025
fdc5dee
Add LanguageFeature
edgarfgp Aug 6, 2025
72385a6
release notes
edgarfgp Aug 6, 2025
4b01e49
update checkInvalidDeclsInTypeDefn
edgarfgp Aug 6, 2025
9289979
Merge branch 'fix-10066' of github.com:edgarfgp/fsharp into fix-10066
edgarfgp Aug 6, 2025
6a0e283
more tests
edgarfgp Aug 6, 2025
634cddf
Merge branch 'main' into fix-10066
edgarfgp Aug 6, 2025
e2a941a
more tests
edgarfgp Aug 6, 2025
26bd14b
Merge branch 'fix-10066' of github.com:edgarfgp/fsharp into fix-10066
edgarfgp Aug 6, 2025
8354b89
Update tests
edgarfgp Aug 6, 2025
89d9608
Merge branch 'main' into fix-10066
edgarfgp Aug 6, 2025
0acdd53
Merge branch 'main' into fix-10066
edgarfgp Aug 6, 2025
0f1c26f
Update src/Compiler/SyntaxTree/ParseHelpers.fs
edgarfgp Aug 9, 2025
b98dbdf
Revert "Update src/Compiler/SyntaxTree/ParseHelpers.fs"
edgarfgp Aug 11, 2025
a9e53fa
Merge branch 'main' into fix-10066
edgarfgp Aug 11, 2025
f7b4c2c
Merge branch 'main' into fix-10066
edgarfgp Aug 12, 2025
bdf8a93
Use LexFilter instead ParseHelper to raise the warnings.
edgarfgp Aug 13, 2025
0b46a40
Update baselines
edgarfgp Aug 13, 2025
6a9ddd9
update tests
edgarfgp Aug 13, 2025
3c00990
No warning - all declarations at same indentation level
edgarfgp Aug 13, 2025
cca0cc8
Update FSComp.txt
edgarfgp Aug 13, 2025
399755a
Add more tests and rename feature flag
edgarfgp Aug 14, 2025
20cd646
update tests
edgarfgp Aug 14, 2025
f625bbb
Update tests
edgarfgp Aug 14, 2025
b55be44
more renaming
edgarfgp Aug 14, 2025
3a37b06
use a simple function instead of an AP
edgarfgp Aug 14, 2025
7585e04
simplify and update baselines
edgarfgp Aug 14, 2025
9589158
update baselines
edgarfgp Aug 14, 2025
efe20da
fix indentation issue found thanks to the new error
edgarfgp Aug 14, 2025
7642629
update release notes
edgarfgp Aug 14, 2025
a412c6b
more bsl
edgarfgp Aug 15, 2025
adcd373
more test updates
edgarfgp Aug 15, 2025
46a9848
Merge branch 'main' into fix-10066
edgarfgp Aug 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Completion: fix previous namespace considered opened [PR #18609](https://github.com/dotnet/fsharp/pull/18609)
* Fix active pattern typechecking regression. ([Issue #18638](https://github.com/dotnet/fsharp/issues/18638), [PR #18642](https://github.com/dotnet/fsharp/pull/18642))
* Fix nullness warnings when casting non-nullable values to `IEquatable<T>` to match C# behavior. ([Issue #18759](https://github.com/dotnet/fsharp/issues/18759))
* Error on invalid declarations in type definitions.([Issue #10066](https://github.com/dotnet/fsharp/issues/10066), [PR #18813](https://github.com/dotnet/fsharp/pull/18813))
* Fix IsByRefLikeAttribute types being incorrectly suppressed in completion lists. Types like `Span<T>` and `ReadOnlySpan<T>` now appear correctly in IntelliSense.

* Fix SRTP nullness constraint resolution for types imported from older assemblies. AmbivalentToNull types now use legacy F# nullness rules instead of always satisfying `'T : null` constraints. ([Issue #18390](https://github.com/dotnet/fsharp/issues/18390), [Issue #18344](https://github.com/dotnet/fsharp/issues/18344))
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
### Fixed

* Warn on uppercase identifiers in patterns. ([PR #15816](https://github.com/dotnet/fsharp/pull/15816))
* Error on invalid declarations in type definitions.([Issue #10066](https://github.com/dotnet/fsharp/issues/10066), [PR #18813](https://github.com/dotnet/fsharp/pull/18813))

### Changed
6 changes: 6 additions & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,11 @@ lexhlpIdentifierReserved,"The identifier '%s' is reserved for future use by F#"
lexfltIncorrentIndentationOfIn,"The indentation of this 'in' token is incorrect with respect to the corresponding 'let'"
lexfltTokenIsOffsideOfContextStartedEarlier,"Unexpected syntax or possible incorrect indentation: this token is offside of context started at position %s. Try indenting this further.\nTo continue using non-conforming indentation, pass the '--strict-indentation-' flag to the compiler, or set the language version to F# 7."
lexfltSeparatorTokensOfPatternMatchMisaligned,"The '|' tokens separating rules of this pattern match are misaligned by one column. Consider realigning your code or using further indentation."
lexfltInvalidNestedTypeDefinition,"Nested type definitions are not allowed. Types must be defined at module or namespace level."
lexfltInvalidNestedModule,"Modules cannot be nested inside types. Define modules at module or namespace level."
lexfltInvalidNestedExceptionDefinition,"Exceptions must be defined at module level, not inside types."
lexfltInvalidNestedOpenDeclaration,"'open' declarations must appear at module level, not inside types."
lexfltInvalidNestedConstruct,"'%s' must be defined at module level, not inside a type."
1123,nrInvalidModuleExprType,"Invalid module/expression/type"
1124,nrTypeInstantiationNeededToDisambiguateTypesWithSameName,"Multiple types exist called '%s', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. '%s'."
1125,nrTypeInstantiationIsMissingAndCouldNotBeInferred,"The instantiation of the generic type '%s' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. '%s'."
Expand Down Expand Up @@ -1802,6 +1807,7 @@ featureSupportWarnWhenUnitPassedToObjArg,"Warn when unit is passed to a member a
featureUseBangBindingValueDiscard,"Allows use! _ = ... in computation expressions"
featureBetterAnonymousRecordParsing,"Support for better anonymous record parsing"
featureScopedNowarn,"Support for scoped enabling / disabling of warnings by #warn and #nowarn directives, also inside modules"
featureErrorOnInvalidDeclsInTypeDefinitions,"Error when invalid declarations are used in type definitions."
featureAllowLetOrUseBangTypeAnnotationWithoutParens,"Allow let! and use! type annotations without requiring parentheses"
3874,lexWarnDirectiveMustBeFirst,"#nowarn/#warnon directives must appear as the first non-whitespace characters on a line"
3875,lexWarnDirectiveMustHaveArgs,"Warn directives must have warning number(s) as argument(s)"
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type LanguageFeature =
| UseBangBindingValueDiscard
| BetterAnonymousRecordParsing
| ScopedNowarn
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal

Expand Down Expand Up @@ -240,6 +241,7 @@ type LanguageVersion(versionText) =
LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion100
LanguageFeature.AllowAccessModifiersToAutoPropertiesGettersAndSetters, languageVersion100
LanguageFeature.ReturnFromFinal, languageVersion100
LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions, languageVersion100

// F# preview (still preview in 10.0)
LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work
Expand Down Expand Up @@ -410,6 +412,7 @@ type LanguageVersion(versionText) =
| LanguageFeature.UseBangBindingValueDiscard -> FSComp.SR.featureUseBangBindingValueDiscard ()
| LanguageFeature.BetterAnonymousRecordParsing -> FSComp.SR.featureBetterAnonymousRecordParsing ()
| LanguageFeature.ScopedNowarn -> FSComp.SR.featureScopedNowarn ()
| LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions -> FSComp.SR.featureErrorOnInvalidDeclsInTypeDefinitions ()
| LanguageFeature.AllowTypedLetUseAndBang -> FSComp.SR.featureAllowLetOrUseBangTypeAnnotationWithoutParens ()
| LanguageFeature.ReturnFromFinal -> FSComp.SR.featureReturnFromFinal ()

Expand Down
1 change: 1 addition & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type LanguageFeature =
| UseBangBindingValueDiscard
| BetterAnonymousRecordParsing
| ScopedNowarn
| ErrorOnInvalidDeclsInTypeDefinitions
| AllowTypedLetUseAndBang
| ReturnFromFinal

Expand Down
106 changes: 106 additions & 0 deletions src/Compiler/SyntaxTree/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,98 @@ type LexFilterImpl (
if debug then dprintf "inserting %+A\n" tok
returnToken (lexbufStateForInsertedDummyTokens (startPosOfTokenTup tokenTup, tokenTup.LexbufState.EndPos)) tok

// Check if we're inappropriately inside a type definition for constructs that shouldn't be there
// This validates that TYPE, MODULE, EXCEPTION, and OPEN declarations are not nested within type definitions
// The check works as follows:
// 1. Only check if the language feature WarnOnUnexpectedModuleDefinitionsInsideTypes is enabled
// 2. Skip validation inside parentheses (to avoid false positives with inline IL)
// 3. Traverse the context stack looking for a CtxtTypeDefns
// 4. If found, check if the current token is indented INSIDE it (greater column, not equal)
// 5. Verify we're not in a legitimate nested context (members, augmentations, or escaped to module/namespace)
// 6. If all conditions match, issue an error message
//
// Note: We don't check 'let' bindings as they can be valid in classes with constructors
// Note: Constructs at the same column level are NOT nested (e.g., type A = A type B = B on same line)
let checkForInvalidDeclsInTypeDefn keyword =
if lexbuf.SupportsFeature LanguageFeature.ErrorOnInvalidDeclsInTypeDefinitions then
// Skip validation if we're inside a parenthesis context
// This avoids false positives with inline IL: (# "unbox.any !0" type ('T) x : 'T #)
let rec hasParenContext stack =
match stack with
| [] -> false
| CtxtParen _ :: _ -> true
| CtxtSeqBlock _ :: rest
| CtxtVanilla _ :: rest -> hasParenContext rest
| _ -> false

// Don't validate if we're in a paren context (could be inline IL or other valid syntax)
if not (hasParenContext offsideStack) then
// Find the nearest type definition context and check if we're inappropriately nested
let rec checkNesting stack typeDefnsSeen =
match stack with
| [] ->
// We've traversed the whole stack without finding issues
false

| CtxtModuleBody _ :: _
| CtxtNamespaceBody _ :: _ ->
// We've escaped to module/namespace level - constructs here are OK
false

| CtxtTypeDefns(typePos, _) :: rest ->
// Found a type definition - check if we're inappropriately inside it
// IMPORTANT: Same-line declarations are sequential, not nested
// Example: type A = A type B = B (all on same line, B is not nested in A)
// Only warn if on a DIFFERENT line with GREATER indentation
if tokenStartPos.Line > typePos.Line && tokenStartCol > typePos.Column then
// We're indented inside the type - this might be invalid
// But first check if we're in a valid member/augmentation context
let rec isInMemberContext s =
match s with
| [] -> false
| CtxtMemberHead _ :: _
| CtxtMemberBody _ :: _ -> true
| CtxtWithAsAugment _ :: _ -> true // Type augmentation with 'with'
| CtxtSeqBlock _ :: tail
| CtxtVanilla _ :: tail -> isInMemberContext tail
| _ -> false

not (isInMemberContext stack)
else
// Not indented inside this type (same column or less), check deeper in the stack
checkNesting rest true

| CtxtSeqBlock _ :: rest
| CtxtVanilla _ :: rest
| CtxtParen _ :: rest ->
// Transparent contexts - continue checking
checkNesting rest typeDefnsSeen

| CtxtMemberHead _ :: _
| CtxtMemberBody _ :: _ when typeDefnsSeen ->
// We're in a member context after seeing a type - this is OK
false

| _ :: rest ->
// Other contexts - continue checking
checkNesting rest typeDefnsSeen

if checkNesting offsideStack false then
let errorMessage =
match keyword with
| "TYPE" ->
FSComp.SR.lexfltInvalidNestedTypeDefinition()
| "MODULE" ->
FSComp.SR.lexfltInvalidNestedModule()
| "EXCEPTION" ->
FSComp.SR.lexfltInvalidNestedExceptionDefinition()
| "OPEN" ->
FSComp.SR.lexfltInvalidNestedOpenDeclaration()
| _ ->
FSComp.SR.lexfltInvalidNestedConstruct(keyword)

error tokenTup errorMessage

let isSemiSemi = match token with SEMICOLON_SEMICOLON -> true | _ -> false
let relaxWhitespace2OffsideRule =
// Offside rule for CtxtLetDecl (in types or modules) / CtxtMemberHead / CtxtTypeDefns... (given RelaxWhitespace2)
Expand Down Expand Up @@ -2019,7 +2111,11 @@ type LexFilterImpl (
returnToken tokenLexbufState token

// module ... ~~~> CtxtModuleHead
// Check for inappropriate nesting within type definitions
| MODULE, _ :: _ ->
// Check if this module definition is inappropriately nested in a type
checkForInvalidDeclsInTypeDefn "MODULE"

insertComingSoonTokens("MODULE", MODULE_COMING_SOON, MODULE_IS_HERE)
if debug then dprintf "MODULE: entering CtxtModuleHead, awaiting EQUALS to go to CtxtSeqBlock (%a)\n" outputPos tokenStartPos
let isNested = match offsideStack with | [ CtxtSeqBlock _ ] -> false | _ -> true
Expand All @@ -2029,6 +2125,8 @@ type LexFilterImpl (

// exception ... ~~~> CtxtException
| EXCEPTION, _ :: _ ->
// Check if this exception definition is inappropriately nested in a type
checkForInvalidDeclsInTypeDefn "EXCEPTION"
if debug then dprintf "EXCEPTION: entering CtxtException(%a)\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtException tokenStartPos)
returnToken tokenLexbufState token
Expand Down Expand Up @@ -2470,6 +2568,9 @@ type LexFilterImpl (
returnToken tokenLexbufState token

| TYPE, _ ->
// Check if this type definition is inappropriately nested in another type
checkForInvalidDeclsInTypeDefn "TYPE"

insertComingSoonTokens("TYPE", TYPE_COMING_SOON, TYPE_IS_HERE)
if debug then dprintf "TYPE, pushing CtxtTypeDefns(%a)\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtTypeDefns(tokenStartPos, None))
Expand All @@ -2490,6 +2591,11 @@ type LexFilterImpl (
| OBLOCKBEGIN, _ ->
returnToken tokenLexbufState token

| OPEN, _ :: _ ->
// Check if this open declaration is inappropriately nested in a type
checkForInvalidDeclsInTypeDefn "OPEN"
returnToken tokenLexbufState token

| ODUMMY _, _ ->
if debug then dprintf "skipping dummy token as no offside rules apply\n"
pool.Return tokenTup
Expand Down
1 change: 1 addition & 0 deletions src/Compiler/SyntaxTree/ParseHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open FSharp.Compiler.Syntax
open FSharp.Compiler.SyntaxTrivia
open FSharp.Compiler.Features
open FSharp.Compiler.Text
open FSharp.Compiler.UnicodeLexing
open FSharp.Compiler.Xml
open Internal.Utilities.Text.Lexing
open Internal.Utilities.Text.Parsing
Expand Down
3 changes: 3 additions & 0 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,9 @@ moduleDefnsOrExpr:

/* A sequence of definitions in a namespace or module */
moduleDefns:
| moduleDefnOrDirective moduleDefnOrDirective
{ $1 @ $2 }

| moduleDefnOrDirective moduleDefns
{ $1 @ $2 }

Expand Down
30 changes: 30 additions & 0 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading