Skip to content
Closed
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
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
<ItemGroup Condition="'$(NoCommonAnalyzers)'!='true'">
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" />
</ItemGroup>

<!-- ALWAYS generate the path property; Though this appears to be a default now, there's no harm in being explicit -->
<ItemDefinitionGroup>
<PackageReference GeneratePathProperty="true" />
</ItemDefinitionGroup>
</When>
<When Condition="'$(MSBuildProjectExtension)'=='.vcxproj'">
<!-- vcxproj uses a different pattern for output paths -->
Expand Down
6 changes: 6 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ItemGroup>
<!-- Roslyn Analyzers ***MUST*** target older framework -->
<PackageVersion Include="AnsiCodes" Version="0.2.1" />
<PackageVersion Include="DiffPlex" Version="1.9.0" />
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis" Version="5.0.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="5.0.0" />
Expand All @@ -32,7 +33,12 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.Features" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.VisualBasic.SourceGenerators.Testing" Version="1.1.2" />
<PackageVersion Include="Basic.Reference.Assemblies" Version="1.8.4" />

<!-- Until https://github.com/jaredpar/basic-reference-assemblies/issues/78 is resolved these are a specific version ref -->
<PackageVersion Include="Basic.Reference.Assemblies.Net80" Version="1.8.3" />
<PackageVersion Include="Basic.Reference.Assemblies.Net90" Version="1.8.4" />
<PackageVersion Include="Basic.Reference.Assemblies.Net100" Version="1.8.4" />
<PackageVersion Include="PolySharp" Version="1.15.0" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.0" />
Expand Down
5 changes: 5 additions & 0 deletions IgnoredWords.dic
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Cmp
Comdat
Comdats
Committers
comparand
compat
Concat
Config
Expand All @@ -67,9 +68,11 @@ downcasts
endian
endianess
endif
endregion
enum
Enums
env
equatability
exe
facepalm
fallback
Expand Down Expand Up @@ -170,6 +173,7 @@ stdcall
struct
structs
Subrange
suppressions
Sym
Tag
telliam
Expand Down Expand Up @@ -205,6 +209,7 @@ Users
usings
utils
validator
validators
varargs
variadic
vcxproj
Expand Down
23 changes: 17 additions & 6 deletions NuGet.Config
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="BuildOutput\packages"/>
</config>
<packageSources>
<add key="BuildOutput" value="BuildOutput\Nuget" />
</packageSources>
<config>
<add key="repositoryPath" value="BuildOutput\packages"/>
</config>
<packageSources>
<clear/>
<add key="BuildOutput" value="BuildOutput\Nuget" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
<package pattern="Ubiquity.NET.Versioning.Build.Tasks" />
</packageSource>
<packageSource key="BuildOutput">
<package pattern="Ubiquity.NET.*" />
</packageSource>
</packageSourceMapping>
</configuration>
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ of multiple small libraries that didn't warrant a distinct repository.

>[!IMPORTANT]
> When editing code in this repository make certain that any extensions or tooling that
> automatically removes trailing whitespace is disabled. It is fine to highlight such cases
> and most of the time remove any. However, there are some tests where a trailing whitespace
> is required and a critical part of the tests.
> ***automatically*** removes trailing whitespace is disabled. It is fine to highlight such
> cases and most of the time remove any. However, there are some tests where a trailing
> whitespace is required and a critical part of the tests.
15 changes: 10 additions & 5 deletions .editorconfig → src/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ indent_size = 4
insert_final_newline = true
tab_width = 4
end_of_line = crlf

# VSSPELL: Spell checker settings for all files
vsspell_section_id = bc80fe46ff7a40189dee3f3476198102
# until [VSSpellChecker bug 277](https://github.com/EWSoftware/VSSpellChecker/issues/277)
# is fixed, disable the analyzers. It's more of a PITA than a help
# VSSPELL: Disable the analyzers until Bug 277 is fixed.
vsspell_code_analyzers_enabled = false

# VSSPELL: Spell checker settings for all files
vsspell_section_id = 1c7003ec377c4bd9bb3c509d29770210
vsspell_ignored_words_1c7003ec377c4bd9bb3c509d29770210 = File:.\IgnoredWords.dic|bar
vsspell_ignored_words_bc80fe46ff7a40189dee3f3476198102 = File:.\IgnoredWords.dic

# match VS generated formatting for MSBuild project files
[*.*proj,*.props,*.targets]
Expand Down Expand Up @@ -105,6 +105,10 @@ csharp_style_prefer_local_over_anonymous_function = true:error
csharp_style_prefer_index_operator = true:error
csharp_style_prefer_range_operator = true:error

# CS0618: Type or member is obsolete
dotnet_diagnostic.CS0618.severity = error
csharp_style_prefer_simple_property_accessors = true:suggestion

# Analysis and refactoring rules for Ubiquity.NET
# Description: Code analysis rules for Ubiquity.NET projects

Expand Down Expand Up @@ -983,7 +987,8 @@ dotnet_diagnostic.CA1303.severity = silent

dotnet_diagnostic.CA1304.severity = warning

dotnet_diagnostic.CA1305.severity = warning
// CA1305: Specify IFormatProvider
dotnet_diagnostic.CA1305.severity = silent

dotnet_diagnostic.CA1306.severity = warning

Expand Down
31 changes: 31 additions & 0 deletions src/Ubiquity.NET.CodeAnalysis.Utils/AttributeDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.

namespace Ubiquity.NET.CodeAnalysis.Utils
{
/// <summary>Utility class to provide extensions to <see cref="AttributeData"/></summary>
public static class AttributeDataExtensions
{
/// <summary>Tests if the full namespace qualified name of the name matches this type</summary>
/// <param name="self">AttributeData to test</param>
/// <param name="fullName">Sequence of parts for the name to test with outer most namespace first (Including the simple name)</param>
/// <returns>true if the name matches and false otherwise</returns>
public static bool IsFullNameMatch( this AttributeData self, NamespaceQualifiedName fullName )
{
return self.AttributeClass is not null
&& self.AttributeClass.Name == fullName.SimpleName
&& self.AttributeClass.GetNamespaceNames()
.SequenceEqual( fullName.NamespaceNames );
}

/// <summary>Gets the <see cref="NamespaceQualifiedName"/> for the attribute</summary>
/// <param name="self">self</param>
/// <returns><see cref="NamespaceQualifiedName"/> for the attribute</returns>
public static NamespaceQualifiedName GetNamespaceQualifiedName( this AttributeData self )
{
return self.AttributeClass is null
? new( [], string.Empty )
: self.AttributeClass.GetNamespaceQualifiedName();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,26 @@ public static string GetDeclaredNamespace(this BaseTypeDeclarationSyntax syntax)
/// <param name="syntax">Syntax to get the name for</param>
/// <param name="includeSelf">Flag to indicate if the type itself is included in the name [Default: <see langword="false"/></param>
/// <returns><see cref="NestedClassName"/> of the syntax or <see langword="null"/></returns>
/// <remarks>
/// <note type="information">
/// The return type is never null if <paramref name="includeSelf"/> is true AND it is a structural type (reference or value)
/// as the name of the type itself is included.
/// </note>
/// </remarks>
public static NestedClassName? GetNestedClassName( this BaseTypeDeclarationSyntax syntax, bool includeSelf = false)
{
// Try and get the parent syntax. If it isn't a type like class/struct, this will be null
TypeDeclarationSyntax? parentSyntax = includeSelf ? syntax as TypeDeclarationSyntax : syntax.Parent as TypeDeclarationSyntax;
TypeDeclarationSyntax? parentSyntax = includeSelf
? syntax as TypeDeclarationSyntax
: syntax.Parent as TypeDeclarationSyntax;

NestedClassName? parentClassInfo = null;

// We can only be nested in class/struct/record

// Keep looping while we're in a supported nested type
while (parentSyntax is not null)
{
// NOTE: due to bug https://github.com/dotnet/roslyn/issues/78042 this
// is not using a local static function to evaluate this in the condition
// of the while loop [Workaround: go back to "old" extension syntax...]
var rawKind = parentSyntax.Kind();
bool isAllowedKind
= rawKind == SyntaxKind.ClassDeclaration
|| rawKind == SyntaxKind.StructDeclaration
|| rawKind == SyntaxKind.RecordDeclaration;

if (!isAllowedKind)
if(!IsAllowedKind( parentSyntax ))
{
break;
}
Expand All @@ -90,14 +89,24 @@ bool isAllowedKind
keyword: parentSyntax.Keyword.ValueText,
name: parentSyntax.Identifier.ToString() + parentSyntax.TypeParameterList,
constraints: parentSyntax.ConstraintClauses.ToString(),
children: parentClassInfo is null ? [] : [parentClassInfo]); // set the child link (null initially)
children: parentClassInfo is null ? [] : [ parentClassInfo ]
); // set the child link (null initially)

// Move to the next outer type
parentSyntax = parentSyntax.Parent as TypeDeclarationSyntax;
}

// return a link to the outermost parent type
return parentClassInfo;

// local static function to test for allowed kinds
static bool IsAllowedKind( TypeDeclarationSyntax parentSyntax )
{
var rawKind = parentSyntax.Kind();
return rawKind == SyntaxKind.ClassDeclaration
|| rawKind == SyntaxKind.StructDeclaration
|| rawKind == SyntaxKind.RecordDeclaration;
}
}
}
}
30 changes: 30 additions & 0 deletions src/Ubiquity.NET.CodeAnalysis.Utils/DebugAssert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.

using System.Diagnostics;

namespace Ubiquity.NET.CodeAnalysis.Utils
{
/// <summary>Utility class to support Debug asserts</summary>
public static class DebugAssert
{
/// <summary>Tests if a structure size is < 16 bytes and generates a debug assertion if not</summary>
/// <typeparam name="T">Type of the struct to test</typeparam>
/// <remarks>
/// <para>This uses a runtime debug assert as it isn't possible to know the size at compile time of a managed struct.
/// The `sizeof` doesn't apply for anything with a managed reference or a native pointer sized member
/// as such sizes depend on the actual runtime used.</para>
/// <note type="important">
/// This function ONLY operates in a debug build. That is, this is the compiler will elide calls to this method
/// at the call site unless the "DEBUG" symbol is defined as it has a <see cref="ConditionalAttribute"/> attached to it.
/// </note>
/// </remarks>
/// <seealso href="https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct"/>
[Conditional( "DEBUG" )]
public static void StructSizeOK<T>( )
where T : struct
{
Debug.Assert( Unsafe.SizeOf<T>() <= 16, $"{nameof( T )} size is > 16 bytes; Make it a class" );
}
}
}
43 changes: 38 additions & 5 deletions src/Ubiquity.NET.CodeAnalysis.Utils/DiagnosticInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace Ubiquity.NET.CodeAnalysis.Utils
/// that is needed for caching. A <see cref="Diagnostic"/> is not, so this record bundles
/// the parameters needed for creation of one and defers the construction until needed.
/// </remarks>
public sealed record DiagnosticInfo
public sealed class DiagnosticInfo
: IEquatable<DiagnosticInfo>
{
#if !NET9_0_OR_GREATER
/// <summary>Initializes a new instance of the <see cref="DiagnosticInfo"/> class.</summary>
Expand All @@ -38,17 +39,17 @@ public DiagnosticInfo(DiagnosticDescriptor descriptor, Location? location, param
{
Descriptor = descriptor;
Location = location;
Params = msgArgs.ToImmutableArray();
Params = [ .. msgArgs ];
}

/// <summary>Gets the parameters for this diagnostic</summary>
public EquatableArray<string> Params { get; }
public ImmutableArray<string> Params { get; }

/// <summary>Gets the descriptor for this diagnostic</summary>
public DiagnosticDescriptor Descriptor { get; }

// Location is an abstract type but all derived types implement IEquatable<T> where T is Location
// Thus a location is equatable even though the base abstract type doesn't implement that interface.
// Microsoft.CodeAnalysis.Location is an abstract type but all derived types implement `IEquatable<T> where T is Location`
// Thus, a location is equatable even though the base abstract type doesn't implement that interface.

/// <summary>Gets the location of the source of this diagnostic</summary>
public Location? Location { get; }
Expand All @@ -59,5 +60,37 @@ public Diagnostic CreateDiagnostic()
{
return Diagnostic.Create(Descriptor, Location, Params.ToArray());
}

/// <inheritdoc/>
public bool Equals( DiagnosticInfo other )
{
return other is not null
&& StructuralComparisons.StructuralEqualityComparer.Equals(Params, other.Params)
&& Descriptor.Equals(other.Descriptor)
&& ( ReferenceEquals(Location, other.Location)
|| (Location is not null && Location.Equals(other.Location))
);
}

/// <inheritdoc/>
public override bool Equals( object obj )
{
return obj is DiagnosticInfo other
&& Equals( other );
}

/// <inheritdoc/>
public override int GetHashCode( )
{
// sadly this will re-hash the hashcode computed for the structure, but there is no way
// to combine the result of a hash with other things. (The overload of Add(int) is private)
// The generic Add<T>() will call the type's GetHashCode() and ignores the implementation of
// IStructuralEquatable.
return HashCode.Combine(
StructuralComparisons.StructuralEqualityComparer.GetHashCode( Params ),
Descriptor,
Location
);
}
}
}
Loading
Loading