Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.

Use the XML exception tag in doc comments (F#) #766

Merged
merged 13 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions src/Common/AssemblyCommon.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>

<PropertyGroup Condition="'$(MSBuildProjectExtension)' == '.fsproj'">
<!-- Enable and treat as error malformed XML doc comment warning (FS3390) -->
<OtherFlags>--warnon:3390</OtherFlags>
<WarningsAsErrors>3390</WarningsAsErrors>
</PropertyGroup>

<!--
When DefineConstants receives a list with the semicolon escaped (%3B),
like: SIGNED%3BTELEMETRY
Expand Down
37 changes: 27 additions & 10 deletions src/QsCompiler/Core/SymbolResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ type SpecializationBundleProperties =
typeArgs
|> QsNullable<_>.Map(fun args -> (args |> Seq.map (fun t -> t.WithoutRangeInfo)).ToImmutableArray())

/// <summary>
/// Returns an identifier for the bundle to which the given specialization declaration belongs to.
/// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization.
/// </summary>
/// <exception cref="InvalidOperationException">No (partial) resolution is defined for the given specialization.</exception>
static member internal BundleId(spec: Resolution<_, _>) =
match spec.Resolved with
| Null -> InvalidOperationException "cannot determine id for unresolved specialization" |> raise
Expand Down Expand Up @@ -386,9 +388,11 @@ module SymbolResolution =
then returnTypeErr :: excessTypeParamWarn |> List.toArray
else excessTypeParamWarn |> List.toArray

/// <summary>
/// Helper function for ResolveCallableSignature that resolves the given argument tuple
/// using the given routine to resolve the declared item types.
/// Throws an ArgumentException if the given argument is a QsTupleItem opposed to a QsTuple.
/// </summary>
/// <exception cref="ArgumentException"><paramref name="arg"/> is a <see cref="QsTupleItem"/> as opposed to a <see cref="QsTuple"/>.</exception>
let private ResolveArgumentTuple (resolveSymbol, resolveType) arg =
let resolveArg (qsSym: QsSymbol, symType) =
let range = qsSym.Range.ValueOr Range.Zero
Expand Down Expand Up @@ -422,11 +426,13 @@ module SymbolResolution =
Range = range
}

/// <summary>
/// Give a list with the characteristics of all specializations as well as a routine for type resolution,
/// fully resolves the given callable signature as well as its argument tuple.
/// The position offset information for variables declared in the argument tuple will be set to Null.
/// Returns the resolved signature and argument tuple, as well as an array with the diagnostics created during resolution.
/// Throws an ArgumentException if the given list of specialization characteristics is empty.
/// </summary>
/// <exception cref="ArgumentException"><paramref name="specBundleInfos"/> is empty.</exception>
let internal ResolveCallableSignature (resolveType, specBundleInfos: CallableInformation list)
(signature: CallableSignature)
=
Expand Down Expand Up @@ -495,10 +501,12 @@ module SymbolResolution =

(resolvedSig, argTuple), [ inErr; outErr; resErrs; tpErrs ] |> Array.concat

/// <summary>
/// Give a routine for type resolution, fully resolves the given user defined type as well as its items.
/// The position offset information for the declared named items will be set to Null.
/// Returns the underlying type as well as the item tuple, along with an array with the diagnostics created during resolution.
/// Throws an ArgumentException if the given type tuple is an empty QsTuple.
/// </summary>
/// <exception cref="ArgumentException"><paramref name="udtTuple"/> is an empty <see cref="QsTuple"/>.</exception>
let internal ResolveTypeDeclaration resolveType (udtTuple: QsTuple<QsSymbol * QsType>) =
let itemDeclarations = new List<LocalVariableDeclaration<string>>()

Expand Down Expand Up @@ -545,6 +553,7 @@ module SymbolResolution =

(underlyingType, argTuple), errs

/// <summary>
/// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file.
/// The resolution consists of replacing all unqualified names for user defined types by their qualified name.
/// Generates an array of diagnostics for the cases where no user defined type of the specified name (qualified or unqualified) can be found.
Expand All @@ -553,8 +562,10 @@ module SymbolResolution =
/// and generates suitable diagnostics if they are not, replacing them by the Q# type denoting an invalid type.
/// Returns the resolved type as well as an array with diagnostics.
/// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is inconsistent with the defined callables.
/// May throw an ArgumentException if no namespace with the given name exists, or the given source file is not listed as source of that namespace.
/// Throws a NotSupportedException if the QsType to resolve contains a MissingType.
/// </summary>
/// <exception cref="NotSupportedException"><paramref name="qsType"/> contains a <see cref="MissingType"/>.</exception>
/// <exception cref="ArgumentException">No namespace with the given name exists.</exception>
/// <exception cref="ArgumentException">The given source file is not listed as source of that namespace.</exception>
let rec internal ResolveType (processUDT, processTypeParameter) (qsType: QsType) =
let resolve = ResolveType(processUDT, processTypeParameter)
let asResolvedType t = ResolvedType.New(true, t)
Expand Down Expand Up @@ -609,13 +620,15 @@ module SymbolResolution =
| InvalidType -> QsTypeKind.InvalidType |> asResolvedType, [||]
| MissingType -> NotSupportedException "missing type cannot be resolved" |> raise

/// <summary>
/// Resolves the given attribute using the given function getAttribute to resolve the type id and expected argument type.
/// Generates suitable diagnostics if a suitable attribute cannot be determined,
/// or if the attribute argument contains expressions that are not supported,
/// or if the resolved argument type does not match the expected argument type.
/// The TypeId in the resolved attribute is set to Null if the unresolved Id is not a valid identifier
/// or if the correct attribute cannot be determined, and is set to the corresponding type identifier otherwise.
/// Throws an ArgumentException if a tuple-valued attribute argument does not contain at least one item.
/// </summary>
/// <exception cref="ArgumentException">A tuple-valued attribute argument does not contain at least one item.</exception>
let internal ResolveAttribute getAttribute (attribute: AttributeAnnotation) =
let asTypedExression range (exKind, exType) =
{
Expand Down Expand Up @@ -1039,13 +1052,15 @@ module SymbolResolution =
then InvalidSetExpr |> ResolvedCharacteristics.New, metadata, errs
else resolved, metadata, errs

/// <summary>
/// Given the signature and source file of a callable as well as all specializations defined for it, constructs
/// a dictionary that contains the bundle properties for each set of type- and set-arguments for which the callable has been specialized.
/// The keys of the dictionary are given by the BundleIds obtained for the type- and set-arguments in question.
/// Calls generateSpecialization for each specialization that is not listed in the given collection of specializations but can be inferred,
/// either based on the declared characteristics of the parent callable or based on other existing specializations.
/// Returns the constructed dictionary as well as an array of diagnostics.
/// Throws an InvalidOperationException if no (partial) resolution is defined for any one of the given specializations.
/// </summary>
/// <exception cref="InvalidOperationException">No (partial) resolution is defined for any one of the given specializations.</exception>
let internal GetBundleProperties generateSpecialization
(parentSignature: Resolution<CallableSignature, _>, source)
(definedSpecs: IEnumerable<_>)
Expand Down Expand Up @@ -1101,11 +1116,13 @@ module SymbolResolution =

props.ToImmutableDictionary(), errs

/// <summary>
/// Given a dictionary that maps the BundleId for each set of type- and set-arguments for which the callable has been specialized
/// to the corresponding bundle properties determines the resolution for the given specialization of the given kind.
/// Returns the resolved generator as well as an array of diagnostics generated during resolution.
/// Throws an InvalidOperationException if no (partial) resolution is defined for the given specialization.
/// Fails with the standard KeyNotFoundException if the given specialization is not part of a specialization bundle in the given properties dictionary.
/// </summary>
/// <exception cref="InvalidOperationException">No (partial) resolution is defined for the given specialization.</exception>
/// <exception cref="KeyNotFoundException">The given specialization is not part of a specialization bundle in the given properties dictionary.</exception>
let internal ResolveGenerator (properties: ImmutableDictionary<_, _>)
(kind, spec: Resolution<QsSpecializationGenerator, ResolvedGenerator>)
=
Expand Down
48 changes: 32 additions & 16 deletions src/QsCompiler/Core/SymbolTable/NamespaceManager.fs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
| None -> error ErrorCode.UnknownNamespace [ qualifier ]
| Some ns -> findQualified ns qualifier

/// <summary>
/// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. The
/// resolution consists of replacing all unqualified names for user defined types by their qualified name.
///
Expand All @@ -237,7 +238,8 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
/// consistent with the defined callables.
///
/// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations.
/// Throws a NotSupportedException if the QsType to resolve contains a MissingType.
/// </summary>
/// <exception cref="NotSupportedException"><paramref name="qsType"/> contains a <see cref="MissingType"/>.</exception>
let resolveType (parent: QsQualifiedName, tpNames, source) qsType checkUdt =
let processUDT =
tryResolveTypeName (parent.Namespace, source)
Expand Down Expand Up @@ -626,6 +628,7 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
let resAttr = attr |> List.fold validateAttributes ([], []) |> snd
resAttr.Reverse() |> ImmutableArray.CreateRange, errs.ToArray()

/// <summary>
/// Fully (i.e. recursively) resolves the given Q# type used within the given parent in the given source file. The
/// resolution consists of replacing all unqualified names for user defined types by their qualified name.
///
Expand All @@ -641,10 +644,12 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
/// consistent with the defined callables.
///
/// May throw an exception if the given parent and/or source file is inconsistent with the defined declarations.
/// Throws a NotSupportedException if the QsType to resolve contains a MissingType.
/// </summary>
/// <exception cref="NotSupportedException"><paramref name="qsType"/> contains a <see cref="MissingType"/>.</exception>
member this.ResolveType (parent: QsQualifiedName, tpNames: ImmutableArray<_>, source: string) (qsType: QsType) =
resolveType (parent, tpNames, source) qsType (fun _ -> [||])

/// <summary>
/// Resolves the underlying type as well as all named and unnamed items for the given type declaration in the
/// specified source file using ResolveType.
///
Expand All @@ -654,8 +659,9 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
/// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is
/// consistent with the defined types.
///
/// May throw an exception if the given parent and/or source file is inconsistent with the defined types. Throws an
/// ArgumentException if the given type tuple is an empty QsTuple.
/// May throw an exception if the given parent and/or source file is inconsistent with the defined types.
/// </summary>
/// <exception cref="ArgumentException"><paramref name="typeTuple"/> is an empty <see cref="QsTuple"/>.</exception>
member private this.ResolveTypeDeclaration (fullName: QsQualifiedName, source, modifiers) typeTuple =
// Currently, type parameters for UDTs are not supported.
let checkAccessibility =
Expand All @@ -666,6 +672,7 @@ type NamespaceManager(syncRoot: IReaderWriterLock,

SymbolResolution.ResolveTypeDeclaration resolveType typeTuple

/// <summary>
/// Given the namespace and the name of the callable that the given signature belongs to, as well as its kind and
/// the source file it is declared in, fully resolves all Q# types in the signature using ResolveType.
///
Expand All @@ -681,8 +688,9 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
/// IMPORTANT: for performance reasons does *not* verify if the given the given parent and/or source file is
/// consistent with the defined callables.
///
/// May throw an exception if the given parent and/or source file is inconsistent with the defined callables. Throws
/// an ArgumentException if the given list of characteristics is empty.
/// May throw an exception if the given parent and/or source file is inconsistent with the defined callables.
/// </summary>
/// <exception cref="ArgumentException"><paramref name="specBundleCharacteristics"/> is empty.</exception>
member private this.ResolveCallableSignature (parentKind, parentName: QsQualifiedName, source, access)
(signature, specBundleCharacteristics)
=
Expand Down Expand Up @@ -757,12 +765,14 @@ type NamespaceManager(syncRoot: IReaderWriterLock,

resolutionDiagnostics.Concat(attributeDiagnostics).ToArray()

/// <summary>
/// Resolves and caches all attached attributes and specialization generation directives for all callables
/// declared in all source files of each namespace, inserting inferred specializations if necessary and removing invalid specializations.
/// Then resolves and caches the signature of the callables themselves.
/// Returns the diagnostics generated upon resolution as well as the root position and file for each diagnostic as tuple.
/// IMPORTANT: does *not* return diagnostics generated for type constructors - suitable diagnostics need to be generated upon type resolution.
/// Throws an InvalidOperationException if the types corresponding to the attributes to resolve have not been resolved.
/// </summary>
/// <exception cref="InvalidOperationException">The types corresponding to the attributes to resolve have not been resolved.</exception>
member private this.CacheCallableResolutions(nsNames: ImmutableHashSet<string>) =
// TODO: this needs to be adapted if we support external specializations
let diagnostics =
Expand Down Expand Up @@ -1014,9 +1024,10 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
finally
syncRoot.ExitReadLock()

/// <summary>
/// Returns the declaration headers for all callables defined in source files, regardless of accessibility.
///
/// Throws an InvalidOperationException if the symbols are not currently resolved.
/// </summary>
/// <exception cref="InvalidOperationException">The symbols are not currently resolved.</exception>
member this.DefinedCallables() =
let notResolvedException = InvalidOperationException "callables are not resolved"
syncRoot.EnterReadLock()
Expand Down Expand Up @@ -1054,10 +1065,11 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
finally
syncRoot.ExitReadLock()

/// <summary>
/// Returns the declaration headers for all callables (either defined in source files or imported from referenced
/// assemblies) that are accessible from source files in the compilation unit.
///
/// Throws an InvalidOperationException if the symbols are not currently resolved.
/// </summary>
/// <exception cref="InvalidOperationException">The symbols are not currently resolved.</exception>
member this.AccessibleCallables() =
Seq.append
(Seq.map (fun callable -> callable, true) (this.DefinedCallables()))
Expand All @@ -1078,9 +1090,10 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
finally
syncRoot.ExitReadLock()

/// <summary>
/// Returns the declaration headers for all types defined in source files, regardless of accessibility.
///
/// Throws an InvalidOperationException if the symbols are not currently resolved.
/// </summary>
/// <exception cref="InvalidOperationException">The symbols are not currently resolved.</exception>
member this.DefinedTypes() =
let notResolvedException = InvalidOperationException "types are not resolved"
syncRoot.EnterReadLock()
Expand Down Expand Up @@ -1117,10 +1130,11 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
finally
syncRoot.ExitReadLock()

/// <summary>
/// Returns the declaration headers for all types (either defined in source files or imported from referenced
/// assemblies) that are accessible from source files in the compilation unit.
///
/// Throws an InvalidOperationException if the symbols are not currently resolved.
/// </summary>
/// <exception cref="InvalidOperationException">The symbols are not currently resolved.</exception>
member this.AccessibleTypes() =
Seq.append
(Seq.map (fun qsType -> qsType, true) (this.DefinedTypes()))
Expand Down Expand Up @@ -1536,11 +1550,13 @@ type NamespaceManager(syncRoot: IReaderWriterLock,
| Identifier (GlobalCallable c, _) -> hash (10, c.Namespace, c.Name)
| kind -> JsonConvert.SerializeObject kind |> hash

/// <summary>
/// Generates a hash containing full type information about all entries in the given source file.
/// All entries in the source file have to be fully resolved beforehand.
/// That hash does not contain any information about the imported namespaces, positional information, or about any documentation.
/// Returns the generated hash as well as a separate hash providing information about the imported namespaces.
/// Throws an InvalidOperationException if the given source file contains unresolved entries.
/// </summary>
/// <exception cref="InvalidOperationException"><paramref name="source"/> contains unresolved entries.</exception>
member this.HeaderHash source =
let invalidOperationEx =
InvalidOperationException "everything needs to be resolved before constructing the HeaderString"
Expand Down
Loading