A Mono.Gendarme fork, built against a recent Mono.Cecil version, one that can load assemblies built with current compilers. Can be used with the Fake.build plugin.
See the head of the release branch for the features in the latest actual release.
In this branch
- Can load .net core assemblies
- Will search the nuget cache for dependencies, though this can take some time as an alternative to using
dotnet publish
to get all the code you want to analyse in one place.
- Will search the nuget cache for dependencies, though this can take some time as an alternative to using
- Will load debug information from embedded symbols or actual
.pdb
files if available even on non-Windows platforms.- The main impact is that the
AvoidLongMethodsRule
works by LoC and not IL against .net core code on all platforms.
- The main impact is that the
- Depending whether the Framework or dotnet tool version is used, the results may differ when faced with the same assembly, because of the different runtime being consulted
- e.g. several types marked
[Serializable]
in the Framework are not so marked atdotnet
, so serialization rules will give different answers
- e.g. several types marked
- Because they use obsolescing functions not present in
netstandard2.0
the followingGendarme.Rules.Security.Cas
rules are only present in the Framework tool build, under theObsolete.Rules.Security.Cas
name:AddMissingTypeInheritanceDemandRule
DoNotExposeMethodsProtectedByLinkDemandRule
DoNotReduceTypeSecurityOnMethodsRule
SecureGetObjectDataOverridesRule
- The obsolete
Gendarme.Rules.Portability.MonoCompatibilityReviewRule
is not implemented in this fork. DefineAZeroValueRule
does not trigger for non-int32 enums that have a suitably typed zero value. This rule should not also be doing the job ofEnumsShouldUseInt32Rule
- Due to IL changes
UseIsOperatorRule
has been tuned to avoid false positives at the cost of missing some failure cases - Due to IL changes
DoNotAssumeIntPtrSizeRule
will give false negatives for simple (and often implicit) casts ofIntPtr
to other integer types. - New rule categories
AltCode.Rules.General
for general purpose rulesJustifySuppressionRule
to check theJustification
property onSuppressMessage
attributePreferStrongNamedAssembliesRule
to replace deprecated/withdrawn FxCop rule Microsoft.Design#CA2210AvoidAssemblySemanticVersionMismatchRule
to insist that the API contract (major, minor, and optionally build if defined for the assembly) match, but the lesser facets, revision and possibly build are free.
AltCode.Rules.PowerShell
for re-implementing the old Microsoft PowerShell FxCop rulesAltCode.Rules.PowerShell.UseOnlyStandardVerbsRule
to replace "Microsoft.PowerShell#PS1001:UseOnlyStandardVerbs"AltCode.Rules.PowerShell.DefineCmdletInTheCorrectNamespaceRule
to replace "Microsoft.PowerShell#PS1011:DefineCmdletInTheCorrectNamespace"
- In the text output, include a specimen global suppression attribute for each issue, for convenience when dealing with remaining intractable issues e.g. arising from code generation
- While
Scope
is not heeded by the Gendarme process, it's there to placate other consumers (which will ignore the foreign rule); the comment indicates the corresponding object type within the Gendarme analysis in case they should ever be out of line. - The syntax and punctuation of the
Target
with regards to nested types and special names is as Gendarme expects, which differs somewhat from FxCop in annoying details - The emitted section looks like this:
- While
Global Suppression Attribute:
[<assembly: SuppressMessage("Gendarme.Rules.Correctness",
"MethodCanBeMadeStaticRule",
Scope = "member", // MethodDefinition
Target = "ParameterNamesShouldMatch.Handler::ShowMessage(a,System.String)",
Justification = "")>]
After having achieved the first objective, of being able to analyze code from the new .net, the next goal of this fork has been to make the tool more F# aware, because that's where I personally use it the most. There are several places where F# code generation emits patterns that are detected by legacy Gendarme as erroneous, but which are not under sufficiently fine control by the developer or cannot be annotated to suppress a warning.
Having resolved many issues stemming from a Cecil change to what the name and namespace properties of a nested type returned, and differences in behaviour under .netstandard
compared with the .net Framework, the remaining sources of test failure are compiler changes (from pre-Roslyn to now) . In particular, switch
on integral values is often indistinguishable from an if
or if
/else if
construct, so some tests have just been set to [Ignore]
The following rule suites currently have unit test failures
- Framework -- 2 failures for Stack entry analysis for which there is no evidence of them ever having worked (even if the code under test is built with a 2008-vintage C# compiler the tests fail in the same way as at net6.0)
- TestMultipleCatch()
- TestTryCatchFinally() -- works with optimized build of C# 2008 (v2.0.50727\csc.exe /o)
- Smells -- 2 failures and 1 ignored due to IL changes
- false positive in
SuccessOnNonDuplicatedCodeIntoForeachLoopTest
(no evidence of ever passing : still fails when the code under test was compiled with a 2008 vintage compiler with or without optimization) - false positive in
SuccessOnNonDuplicatedInSwitchLoadingByFieldsTest
(Roslyn inducedswitch
statement changes -- the test succeeds with the same code built with a 2008 vintage compiler) AvoidSwitchStatementsRule
test,FailOnMethodWithSwitchTest
has similarly been[Ignore]
d because of the major IL changes involved (now decompiles to anif
expression).
- false positive in
For the moment this seems to suffice to tame unreasonable, or unfixable generated, issues --
- Fix
AvoidMultidimensionalIndexerRule
for F# generated parameterless methods calledget_Item
- Fix comparison of nested type names in parameters against supplied types
- Ignore
[CompilerGenerated]
methods forAvoidSwitchStatementsRule
andCheckParametersNullityInVisibleMethodsRule
- Ignore
[CompilerGenerated]
fields and methods forVariableNamesShouldNotMatchFieldNamesRule
- Ignore
<StartupCode$
names inUseCorrectCasingRule
- Ignore generated types containg
@
in their names forAvoidUnsealedUninheritedInternalTypesRule
- Ignore the
Tags
generated type inside union types forAvoidVisibleConstantFieldRule
- Explicitly exempt the
get_
andset_
prefixes of getter and setter methods from theAvoidNonAlphanumericIdentifierRule
since that was not already a thing. - Make
AvoidUnneededUnboxingRule
not applicable to[CompilerGenerated]
functions (e.g. Union caseCompareTo
) - Exempt debugger-related generated types related to union types from
AvoidUnsealedUninheritedInternalTypeRule
andUseCorrectCasingRule
- Exempt getter and setter methods from
ConsiderConvertingMethodToPropertyRule
(well, duh!) - Exempt types with only fully
[CompilerGenerated]
Equals
andCompareTo
methods fromImplementIComparableCorrectlyRule
; also explicitly exempt record types - Even if a union type is
[Obsolete]
don't bother telling us its cases and case constructors depend on it. - In F# code (type with F#
[CompilationMapping]
attribute) then allow single lower-case letter generic types - Exempt methods of F# generated types with
@
in the name fromParameterNamesShouldMatchOverriddenMethodRule
- Exempt match on union types from
AvoidSwitchStatementsRule
- Exempt F# generated types with
@
in the name fromUseCorrectPrefixRule
,VariableNamesShouldNotMatchFieldNamesRule
andUseCorrectCasingRule
- Exempt generated abstract closure types from
AbstractTypesShouldNotHavePublicConstructorsRule
- Exempt constructors of record types, or generated types with
@
in the name, fromAvoidLongParameterListsRule
- Exempt generated types with
@
in their names fromAvoidUnnecessarySpecializationRule
,AvoidSpeculativeGeneralityRule
andMethodCanBeMadeStaticRule
- Exempt F# placeholder arguments
_
(compiled to_arg...
) fromUseCorrectCasingRule
- Exempt module-bound functions from
ConsiderConvertingMethodToPropertyRule
- Exempt fields and constructors of records from
RemoveDependenceOnObsoleteCodeRule
; accessors will still be caught but can be[SuppressMessage]
d as needed - Take account of F#'s habit of making a virtual call to the base type constructor in object types constructors.
- Exempt F# code in modules, or where a
match
could equally be anif
fromAvoidSwitchStatementsRule
,match
being idiomatic and occasionally just happening to be on an explicit integral type - Exempt property backing fields for code like
member val LocalSource = false with get, set
fromAvoidUnneededFieldInitializationRule
- Exempt union cases (unsealed but not likely to be inherited) from
AvoidUnsealedUninheritedInternalTypeRule
- Exempt generated types with "@" in the name from
AvoidMethodWithUnusedGenericTypeRule
- Exempt the field accessors of F# types (usually bypassed by the compiler by code that goes direct to the backing field) from
AvoidUncalledPrivateCodeRule
- Allow for F# extension properties (
TypeName.get_...
andTypeName.set_...
) and for generated parameter names of the form_arg...
inAvoidNonAlphanumericIdentifierRule
- Consider F# extension methods/properties to be object-bound rather than module bound for
UseCorrectCasingRule
- Add a heuristic to recognise F# compiler generated disposal after a
use
inEnsureLocalDisposalRule
- Add a
RelaxedAvoidCodeDuplicatedInSameClassRule
, which looks for patterns aligning with visible sequence points, and excludes patterns containingthrow new ArgumentNullException(...)
- For
AvoidLargeClassesRule
, ignoreFSharpFunc
andFSharpTypeFunc
valued fields in generated types with@
in their names; treat them as methods in the type instead. - For
AvoidDeepNamespaceHierarchyRule
, ignore F# generated namespaces of the form<StartupCode$a-b-c-d>.$.NETFramework,Version=...
- For
AvoidRepetitiveCastsRule
, ignore F#is
thenas
of anonymous temporaries (often happens inmatch
expressions on sum types) - Adapt
UseCorrectCasingRule
to be compatible withFSharpLint
for F# code (Non-class, non-public, functions should be camel-cased) - Add a
RelaxedMarkAllNonSerializableFieldsRule
which ignores F# types with@
in the name, keeping the full-strength version for cases where serializing a closure is intentional. - Skip types called
<PrivateImplementationDetails>
- Don't apply
ParameterNamesShouldMatchOverridenMethodRule
to cases where the base method has a null or empty parameter name (e.g. F# interfaces) - Don't apply
DoNotDeclareVirtualMethodsInSealedTypeRule
to F# closure types - Don't apply
PreferStringComparisonOverrideRule
to generated code - Don't apply
Gendarme.Rules.Gendarme.UseCorrectSuffixRule
to types in namespaces beginning "<StartupCode$
"
Build | GitHub | |
Coverage | Coveralls |
Assumes net8.0/VS2022 build environment
dotnet tool restore
dotnet run --project ./Build/Setup.fsproj
dotnet run --project ./Build/Build.fsproj
The build
stage can be done in Visual Studio with the Debug configuration to run the unit tests
- Coveralls for allowing free services for Open Source projects