diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 02136c45fd0..2207a4792eb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -233,6 +233,19 @@ jobs: - script: eng\CIBuild.cmd -configuration Release -noSign /p:DotNetBuildFromSource=true /p:FSharpSourceBuild=true displayName: Build + # Up-to-date + - job: UpToDate_Windows + pool: + vmImage: windows-2019 + steps: + - checkout: self + clean: true + - task: PowerShell@2 + displayName: Run up-to-date build check + inputs: + filePath: eng\tests\UpToDate.ps1 + arguments: -configuration $(_BuildConfig) -ci -binaryLog + #---------------------------------------------------------------------------------------------------------------------# # FCS builds # #---------------------------------------------------------------------------------------------------------------------# diff --git a/eng/tests/UpToDate.ps1 b/eng/tests/UpToDate.ps1 new file mode 100644 index 00000000000..a7e50a26d60 --- /dev/null +++ b/eng/tests/UpToDate.ps1 @@ -0,0 +1,70 @@ +# This script verifies that subsequent calls to `Build.cmd` don't cause assemblies to be unnecessarily rebuilt. + +[CmdletBinding(PositionalBinding=$false)] +param ( + [string][Alias('c')]$configuration = "Debug", + [parameter(ValueFromRemainingArguments=$true)][string[]]$properties +) + +Set-StrictMode -version 2.0 +$ErrorActionPreference = "Stop" + +try { + $RepoRoot = Join-Path $PSScriptRoot ".." | Join-Path -ChildPath ".." -Resolve + $BuildScript = Join-Path $RepoRoot "Build.cmd" + + # do first build + & $BuildScript -configuration $configuration @properties + if ($LASTEXITCODE -ne 0) { + Write-Host "Error running first build." + exit 1 + } + + # gather assembly timestamps + $ArtifactsBinDir = Join-Path $RepoRoot "artifacts" | Join-Path -ChildPath "bin" -Resolve + $FSharpAssemblyDirs = Get-ChildItem -Path $ArtifactsBinDir -Filter "FSharp.*" + $FSharpAssemblyPaths = $FSharpAssemblyDirs | ForEach-Object { Get-ChildItem -Path (Join-Path $ArtifactsBinDir $_) -Recurse -Filter "$_.dll" } | ForEach-Object { $_.FullName } + + $InitialAssembliesAndTimes = @{} + foreach ($asm in $FSharpAssemblyPaths) { + $LastWriteTime = (Get-Item $asm).LastWriteTimeUtc + $InitialAssembliesAndTimes.Add($asm, $LastWriteTime) + } + + $InitialCompiledCount = $FSharpAssemblyPaths.Length + + # build again + & $BuildScript -configuration $configuration @properties + if ($LASTEXITCODE -ne 0) { + Write-Host "Error running second build." + exit 1 + } + + # gather assembly timestamps again + $FinalAssembliesAndTimes = @{} + foreach ($asm in $FSharpAssemblyPaths) { + $LastWriteTime = (Get-Item $asm).LastWriteTimeUtc + $FinalAssembliesAndTimes.Add($asm, $LastWriteTime) + } + + # validate that assembly timestamps haven't changed + $RecompiledFiles = @() + foreach ($asm in $InitialAssembliesAndTimes.keys) { + $InitialTime = $InitialAssembliesAndTimes[$asm] + $FinalTime = $FinalAssembliesAndTimes[$asm] + if ($InitialTime -ne $FinalTime) { + $RecompiledFiles += $asm + } + } + + $RecompiledCount = $RecompiledFiles.Length + Write-Host "$RecompiledCount of $InitialCompiledCount assemblies were re-compiled" + $RecompiledFiles | ForEach-Object { Write-Host " $_" } + exit $RecompiledCount +} +catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 +} diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 7cfdbcf494a..a125a64d7bf 100755 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -764,7 +764,13 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, // 'type C = class ... ' limited by 'type' // 'type C = interface ... ' limited by 'type' // 'type C = struct ... ' limited by 'type' - | _, (CtxtParen ((CLASS | STRUCT | INTERFACE), _) :: CtxtSeqBlock _ :: (CtxtTypeDefns _ as limitCtxt) :: _) + | _, (CtxtParen ((CLASS | STRUCT | INTERFACE), _) :: CtxtSeqBlock _ :: (CtxtTypeDefns _ as limitCtxt) :: _) + // 'type C(' limited by 'type' + | _, (CtxtSeqBlock _ :: CtxtParen(LPAREN, _) :: (CtxtTypeDefns _ as limitCtxt) :: _ ) + // 'static member C(' limited by 'static', likewise others + | _, (CtxtSeqBlock _ :: CtxtParen(LPAREN, _) :: (CtxtMemberHead _ as limitCtxt) :: _ ) + // 'static member P with get() = ' limited by 'static', likewise others + | _, (CtxtWithAsLet _ :: (CtxtMemberHead _ as limitCtxt) :: _ ) -> PositionWithColumn(limitCtxt.StartPos, limitCtxt.StartCol + 1) // REVIEW: document these @@ -780,6 +786,7 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, // else expr | (CtxtIf _ | CtxtElse _ | CtxtThen _), (CtxtIf _ as limitCtxt) :: _rest -> PositionWithColumn(limitCtxt.StartPos, limitCtxt.StartCol) + // Permitted inner-construct precise block alignment: // while ... // do expr diff --git a/src/fsharp/service/ServiceAnalysis.fs b/src/fsharp/service/ServiceAnalysis.fs index f66d372bc28..e4f942b657a 100644 --- a/src/fsharp/service/ServiceAnalysis.fs +++ b/src/fsharp/service/ServiceAnalysis.fs @@ -99,17 +99,27 @@ module UnusedOpens = | :? FSharpMemberOrFunctionOrValue as fv when fv.IsExtensionMember -> // Extension members should be taken into account even though they have a prefix (as they do most of the time) true + | :? FSharpMemberOrFunctionOrValue as fv when not fv.IsModuleValueOrMember -> // Local values can be ignored false + | :? FSharpMemberOrFunctionOrValue when su.IsFromDefinition -> // Value definitions should be ignored false + | :? FSharpGenericParameter -> // Generic parameters can be ignored, they never come into scope via 'open' false + | :? FSharpUnionCase when su.IsFromDefinition -> false + + | :? FSharpField as field when + field.DeclaringEntity.IsSome && field.DeclaringEntity.Value.IsFSharpRecord -> + // Record fields are used in name resolution + true + | _ -> // For the rest of symbols we pick only those which are the first part of a long ident, because it's they which are // contained in opened namespaces / modules. For example, we pick `IO` from long ident `IO.File.OpenWrite` because diff --git a/src/fsharp/symbols/SymbolPatterns.fs b/src/fsharp/symbols/SymbolPatterns.fs index 2265682692b..6c2286b4095 100644 --- a/src/fsharp/symbols/SymbolPatterns.fs +++ b/src/fsharp/symbols/SymbolPatterns.fs @@ -132,8 +132,8 @@ module Symbol = #endif let (|Enum|_|) (entity: FSharpEntity) = if entity.IsEnum then Some() else None - let (|Tuple|_|) (ty: FSharpType option) = - ty |> Option.bind (fun ty -> if ty.IsTupleType then Some() else None) + let (|Tuple|_|) (ty: FSharpType) = + if ty.IsTupleType then Some() else None let (|RefCell|_|) (ty: FSharpType) = match getAbbreviatedType ty with diff --git a/src/fsharp/symbols/SymbolPatterns.fsi b/src/fsharp/symbols/SymbolPatterns.fsi index c4c242270cd..ca3659f34d6 100644 --- a/src/fsharp/symbols/SymbolPatterns.fsi +++ b/src/fsharp/symbols/SymbolPatterns.fsi @@ -37,7 +37,7 @@ module public Symbol = val (|ProvidedAndErasedType|_|) : FSharpEntity -> unit option #endif val (|Enum|_|) : FSharpEntity -> unit option - val (|Tuple|_|) : FSharpType option -> unit option + val (|Tuple|_|) : FSharpType -> unit option val (|RefCell|_|) : FSharpType -> unit option val (|FunctionType|_|) : FSharpType -> unit option val (|Pattern|_|) : FSharpSymbol -> unit option diff --git a/tests/fsharp/typecheck/sigs/neg77.bsl b/tests/fsharp/typecheck/sigs/neg77.bsl index 922e6b66950..3598ff0c935 100644 --- a/tests/fsharp/typecheck/sigs/neg77.bsl +++ b/tests/fsharp/typecheck/sigs/neg77.bsl @@ -4,3 +4,15 @@ neg77.fsx(134,15,134,16): parse error FS0058: Possible incorrect indentation: th neg77.fsx(134,15,134,16): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (133:19). Try indenting this token further or using standard formatting conventions. neg77.fsx(134,15,134,16): parse error FS0010: Unexpected symbol '|' in expression + +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. diff --git a/tests/fsharp/typecheck/sigs/neg77.fsx b/tests/fsharp/typecheck/sigs/neg77.fsx index c354321cde8..2f38101f2d6 100644 --- a/tests/fsharp/typecheck/sigs/neg77.fsx +++ b/tests/fsharp/typecheck/sigs/neg77.fsx @@ -243,6 +243,60 @@ do Application.Run(form) #endif +open System + +type OffsideCheck(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = + static member M(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = 1 + +module M = + type OffsideCheck(a:int, + b:int, c:int, // warning + d:int, e:int, + f:int) = + class end + +module M2 = + type OffsideCheck() = + static member M(a:int, + b:int, c:int, // warning + d:int, e:int, + f:int) = 1 + +type C() = + static member P with get() = + 1 // no warning + +module M3 = + type C() = + static member P with get() = + 1 // warning + + +type OffsideCheck2(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = + static member M(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = 1 + +type OffsideCheck3(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = + static member M(a:int, + b:int, c:int, // no warning + d:int, e:int, + f:int) = 1 + + diff --git a/tests/fsharp/typecheck/sigs/neg77.vsbsl b/tests/fsharp/typecheck/sigs/neg77.vsbsl index 44832298057..32adc1f3b96 100644 --- a/tests/fsharp/typecheck/sigs/neg77.vsbsl +++ b/tests/fsharp/typecheck/sigs/neg77.vsbsl @@ -5,10 +5,34 @@ neg77.fsx(134,15,134,16): parse error FS0058: Possible incorrect indentation: th neg77.fsx(134,15,134,16): parse error FS0010: Unexpected symbol '|' in expression +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. + neg77.fsx(134,15,134,16): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (133:19). Try indenting this token further or using standard formatting conventions. neg77.fsx(134,15,134,16): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (133:19). Try indenting this token further or using standard formatting conventions. neg77.fsx(134,15,134,16): parse error FS0010: Unexpected symbol '|' in expression +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(259,3,259,4): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (258:5). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(267,7,267,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (266:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. + +neg77.fsx(278,7,278,8): parse error FS0058: Possible incorrect indentation: this token is offside of context started at position (277:9). Try indenting this token further or using standard formatting conventions. + neg77.fsx(153,75,153,79): typecheck error FS0001: The type 'Planet * 'a' is not compatible with the type 'Planet' diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 08f19cbe708..9dca0cd56f6 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -5523,6 +5523,14 @@ type UseTheThings(i:int) = member x.UseSomeUsedModuleContainingActivePattern(ActivePattern g) = g member x.UseSomeUsedModuleContainingExtensionMember() = (3).Q member x.UseSomeUsedModuleContainingUnion() = A + +module M1 = + type R = { Field: int } + +module M2 = + open M1 + + let foo x = x.Field """ let fileSource1 = FSharp.Compiler.Text.SourceText.ofString fileSource1Text File.WriteAllText(fileName1, fileSource1Text)