/* MIT License Copyright (c) 2016 JetBrains http://www.jetbrains.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; // ReSharper disable All #pragma warning disable 1591 // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedAutoPropertyAccessor.Global // ReSharper disable IntroduceOptionalParameters.Global // ReSharper disable MemberCanBeProtected.Global // ReSharper disable InconsistentNaming namespace JetBrains.Annotations { /// /// Indicates that the value of the marked element could be null sometimes, /// so the check for null is necessary before its usage. /// /// /// [CanBeNull] object Test() => null; /// /// void UseTest() { /// var p = Test(); /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' /// } /// [AttributeUsage( AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] internal sealed class CanBeNullAttribute : Attribute { } /// /// Indicates that the value of the marked element could never be null. /// /// /// [NotNull] object Foo() { /// return null; // Warning: Possible 'null' assignment /// } /// [AttributeUsage( AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] internal sealed class NotNullAttribute : Attribute { } /// /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property /// or of the Lazy.Value property can never be null. /// [AttributeUsage( AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field)] internal sealed class ItemNotNullAttribute : Attribute { } /// /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property /// or of the Lazy.Value property can be null. /// [AttributeUsage( AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field)] internal sealed class ItemCanBeNullAttribute : Attribute { } /// /// Implicitly apply [NotNull]/[ItemNotNull] annotation to all the of type members and parameters /// in particular scope where this annotation is used (type declaration or whole assembly). /// [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Assembly)] internal sealed class ImplicitNotNullAttribute : Attribute { } /// /// Indicates that the marked method builds string by format pattern and (optional) arguments. /// Parameter, which contains format string, should be given in constructor. The format string /// should be in -like form. /// /// /// [StringFormatMethod("message")] /// void ShowError(string message, params object[] args) { /* do something */ } /// /// void Foo() { /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string /// } /// [AttributeUsage( AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)] internal sealed class StringFormatMethodAttribute : Attribute { /// /// Specifies which parameter of an annotated method should be treated as format-string /// public StringFormatMethodAttribute([NotNull] string formatParameterName) { FormatParameterName = formatParameterName; } [NotNull] public string FormatParameterName { get; private set; } } /// /// For a parameter that is expected to be one of the limited set of values. /// Specify fields of which type should be used as values for this parameter. /// [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] internal sealed class ValueProviderAttribute : Attribute { public ValueProviderAttribute([NotNull] string name) { Name = name; } [NotNull] public string Name { get; private set; } } /// /// Indicates that the function argument should be string literal and match one /// of the parameters of the caller function. For example, ReSharper annotates /// the parameter of . /// /// /// void Foo(string param) { /// if (param == null) /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol /// } /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InvokerParameterNameAttribute : Attribute { } /// /// Indicates that the method is contained in a type that implements /// System.ComponentModel.INotifyPropertyChanged interface and this method /// is used to notify that some property value changed. /// /// /// The method should be non-static and conform to one of the supported signatures: /// /// NotifyChanged(string) /// NotifyChanged(params string[]) /// NotifyChanged{T}(Expression{Func{T}}) /// NotifyChanged{T,U}(Expression{Func{T,U}}) /// SetProperty{T}(ref T, T, string) /// /// /// /// public class Foo : INotifyPropertyChanged { /// public event PropertyChangedEventHandler PropertyChanged; /// /// [NotifyPropertyChangedInvocator] /// protected virtual void NotifyChanged(string propertyName) { ... } /// /// string _name; /// /// public string Name { /// get { return _name; } /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } /// } /// } /// /// Examples of generated notifications: /// /// NotifyChanged("Property") /// NotifyChanged(() => Property) /// NotifyChanged((VM x) => x.Property) /// SetProperty(ref myField, value, "Property") /// /// [AttributeUsage(AttributeTargets.Method)] internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute { public NotifyPropertyChangedInvocatorAttribute() { } public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) { ParameterName = parameterName; } public string? ParameterName { get; private set; } } /// /// Describes dependency between method input and output. /// /// ///

Function Definition Table syntax:

/// /// FDT ::= FDTRow [;FDTRow]* /// FDTRow ::= Input => Output | Output <= Input /// Input ::= ParameterName: Value [, Input]* /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} /// Value ::= true | false | null | notnull | canbenull /// /// If method has single input parameter, it's name could be omitted.
/// Using halt (or void/nothing, which is the same) /// for method output means that the methods doesn't return normally.
/// canbenull annotation is only applicable for output parameters.
/// You can use multiple [ContractAnnotation] for each FDT row, /// or use single attribute with rows separated by semicolon.
///
/// /// /// [ContractAnnotation("=> halt")] /// public void TerminationMethod() /// /// /// [ContractAnnotation("halt <= condition: false")] /// public void Assert(bool condition, string text) // regular assertion method /// /// /// [ContractAnnotation("s:null => true")] /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() /// /// /// // A method that returns null if the parameter is null, /// // and not null if the parameter is not null /// [ContractAnnotation("null => null; notnull => notnull")] /// public object Transform(object data) /// /// /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] /// public bool TryParse(string s, out Person result) /// /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] internal sealed class ContractAnnotationAttribute : Attribute { public ContractAnnotationAttribute([NotNull] string contract) : this(contract, false) { } public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) { Contract = contract; ForceFullStates = forceFullStates; } [NotNull] public string Contract { get; private set; } public bool ForceFullStates { get; private set; } } /// /// Indicates that marked element should be localized or not. /// /// /// [LocalizationRequiredAttribute(true)] /// class Foo { /// string str = "my string"; // Warning: Localizable string /// } /// [AttributeUsage(AttributeTargets.All)] internal sealed class LocalizationRequiredAttribute : Attribute { public LocalizationRequiredAttribute() : this(true) { } public LocalizationRequiredAttribute(bool required) { Required = required; } public bool Required { get; private set; } } /// /// Indicates that the value of the marked type (or its derivatives) /// cannot be compared using '==' or '!=' operators and Equals() /// should be used instead. However, using '==' or '!=' for comparison /// with null is always permitted. /// /// /// [CannotApplyEqualityOperator] /// class NoEquality { } /// /// class UsesNoEquality { /// void Test() { /// var ca1 = new NoEquality(); /// var ca2 = new NoEquality(); /// if (ca1 != null) { // OK /// bool condition = ca1 == ca2; // Warning /// } /// } /// } /// [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } /// /// When applied to a target attribute, specifies a requirement for any type marked /// with the target attribute to implement or inherit specific type or types. /// /// /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement /// class ComponentAttribute : Attribute { } /// /// [Component] // ComponentAttribute requires implementing IComponent interface /// class MyComponent : IComponent { } /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [BaseTypeRequired(typeof(Attribute))] internal sealed class BaseTypeRequiredAttribute : Attribute { public BaseTypeRequiredAttribute([NotNull] Type baseType) { BaseType = baseType; } [NotNull] public Type BaseType { get; private set; } } /// /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), /// so this symbol will not be marked as unused (as well as by other usage inspections). /// [AttributeUsage(AttributeTargets.All)] internal sealed class UsedImplicitlyAttribute : Attribute { public UsedImplicitlyAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } public ImplicitUseKindFlags UseKindFlags { get; private set; } public ImplicitUseTargetFlags TargetFlags { get; private set; } } /// /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes /// as unused (as well as by other usage inspections) /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] internal sealed class MeansImplicitUseAttribute : Attribute { public MeansImplicitUseAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } } [Flags] internal enum ImplicitUseKindFlags { Default = Access | Assign | InstantiatedWithFixedConstructorSignature, /// Only entity marked with attribute considered used. Access = 1, /// Indicates implicit assignment to a member. Assign = 2, /// /// Indicates implicit instantiation of a type with fixed constructor signature. /// That means any unused constructor parameters won't be reported as such. /// InstantiatedWithFixedConstructorSignature = 4, /// Indicates implicit instantiation of a type. InstantiatedNoFixedConstructorSignature = 8 } /// /// Specify what is considered used implicitly when marked /// with or . /// [Flags] internal enum ImplicitUseTargetFlags { Default = Itself, Itself = 1, /// Members of entity marked with attribute are considered used. Members = 2, /// Entity marked with attribute and all its members considered used. WithMembers = Itself | Members } /// /// This attribute is intended to mark publicly available API /// which should not be removed and so is treated as used. /// [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] internal sealed class PublicAPIAttribute : Attribute { public PublicAPIAttribute() { } public PublicAPIAttribute([NotNull] string comment) { Comment = comment; } public string? Comment { get; private set; } } /// /// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. /// If the parameter is a delegate, indicates that the delegate can only be invoked during method execution /// (the delegate can be invoked zero or multiple times, but not stored to some field and invoked later, /// when the containing method is no longer on the execution stack). /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. /// If is true, the attribute will only take effect /// if the method invocation is located under the await expression. /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class InstantHandleAttribute : Attribute { /// /// Requires the method invocation to be used under the await expression for this attribute to take effect. /// Can be used for delegate/enumerable parameters of async methods. /// public bool RequireAwait { get; set; } } /// /// This annotation allows enforcing allocation-less usage patterns of delegates for performance-critical APIs. /// When this annotation is applied to the parameter of a delegate type, /// the IDE checks the input argument of this parameter: /// * When a lambda expression or anonymous method is passed as an argument, the IDE verifies that the passed closure /// has no captures of the containing local variables and the compiler is able to cache the delegate instance /// to avoid heap allocations. Otherwise, a warning is produced. /// * The IDE warns when the method name or local function name is passed as an argument because this always results /// in heap allocation of the delegate instance. /// /// /// In C# 9.0+ code, the IDE will also suggest annotating the anonymous functions with the static modifier /// to make use of the similar analysis provided by the language/compiler. /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RequireStaticDelegateAttribute : Attribute { public bool IsError { get; set; } } /// /// Indicates that a method does not make any observable state changes. /// The same as System.Diagnostics.Contracts.PureAttribute. /// /// /// [Pure] int Multiply(int x, int y) => x * y; /// /// void M() { /// Multiply(123, 42); // Waring: Return value of pure method is not used /// } /// [AttributeUsage(AttributeTargets.Method)] internal sealed class PureAttribute : Attribute { } /// /// Indicates that the return value of method invocation must be used. /// [AttributeUsage(AttributeTargets.Method)] internal sealed class MustUseReturnValueAttribute : Attribute { public MustUseReturnValueAttribute() { } public MustUseReturnValueAttribute([NotNull] string justification) { Justification = justification; } public string? Justification { get; private set; } } /// /// Indicates the type member or parameter of some type, that should be used instead of all other ways /// to get the value that type. This annotation is useful when you have some "context" value evaluated /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. /// /// /// class Foo { /// [ProvidesContext] IBarService _barService = ...; /// /// void ProcessNode(INode node) { /// DoSomething(node, node.GetGlobalServices().Bar); /// // ^ Warning: use value of '_barService' field /// } /// } /// [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] internal sealed class ProvidesContextAttribute : Attribute { } /// /// Indicates that a parameter is a path to a file or a folder within a web project. /// Path can be relative or absolute, starting from web root (~). /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class PathReferenceAttribute : Attribute { public PathReferenceAttribute() { } public PathReferenceAttribute([NotNull, PathReference] string basePath) { BasePath = basePath; } public string? BasePath { get; private set; } } /// /// Indicates how method, constructor invocation or property access /// over collection type affects content of the collection. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] internal sealed class CollectionAccessAttribute : Attribute { public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { CollectionAccessType = collectionAccessType; } public CollectionAccessType CollectionAccessType { get; private set; } } [Flags] internal enum CollectionAccessType { /// Method does not use or modify content of the collection. None = 0, /// Method only reads content of the collection but does not modify it. Read = 1, /// Method can change content of the collection but does not add new elements. ModifyExistingContent = 2, /// Method can add new elements to the collection. UpdatedContent = ModifyExistingContent | 4 } /// /// Indicates that the marked method is assertion method, i.e. it halts control flow if /// one of the conditions is satisfied. To set the condition, mark one of the parameters with /// attribute. /// [AttributeUsage(AttributeTargets.Method)] internal sealed class AssertionMethodAttribute : Attribute { } /// /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters /// of delegate type by analyzing LINQ method chains. /// [AttributeUsage(AttributeTargets.Method)] internal sealed class LinqTunnelAttribute : Attribute { } /// /// Indicates that IEnumerable, passed as parameter, is not enumerated. /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class NoEnumerationAttribute : Attribute { } /// /// Indicates that parameter is regular expression pattern. /// [AttributeUsage(AttributeTargets.Parameter)] internal sealed class RegexPatternAttribute : Attribute { } /// /// /// Defines the code search template using the Structural Search and Replace syntax. /// It allows you to find and, if necessary, replace blocks of code that match a specific pattern. /// Search and replace patterns consist of a textual part and placeholders. /// Textural part must contain only identifiers allowed in the target language and will be matched exactly (white spaces, tabulation characters, and line breaks are ignored). /// Placeholders allow matching variable parts of the target code blocks. /// A placeholder has the following format: $placeholder_name$- where placeholder_name is an arbitrary identifier. /// /// /// Available placeholders: /// /// $this$ - expression of containing type /// $thisType$ - containing type /// $member$ - current member placeholder /// $qualifier$ - this placeholder is available in the replace pattern and can be used to insert a qualifier expression matched by the $member$ placeholder. /// (Note that if $qualifier$ placeholder is used, then $member$ placeholder will match only qualified references) /// $expression$ - expression of any type /// $identifier$ - identifier placeholder /// $args$ - any number of arguments /// $arg$ - single argument /// $arg1$ ... $arg10$ - single argument /// $stmts$ - any number of statements /// $stmt$ - single statement /// $stmt1$ ... $stmt10$ - single statement /// $name{Expression, 'Namespace.FooType'}$ - expression with 'Namespace.FooType' type /// $expression{'Namespace.FooType'}$ - expression with 'Namespace.FooType' type /// $name{Type, 'Namespace.FooType'}$ - 'Namespace.FooType' type /// $type{'Namespace.FooType'}$ - 'Namespace.FooType' type /// $statement{1,2}$ - 1 or 2 statements /// /// /// /// Note that you can also define your own placeholders of the supported types and specify arguments for each placeholder type. /// This can be done using the following format: $name{type, arguments}$. Where 'name' - is the name of your placeholder, /// 'type' - is the type of your placeholder (one of the following: Expression, Type, Identifier, Statement, Argument, Member), /// 'arguments' - arguments list for your placeholder. Each placeholder type supports its own arguments, check examples below for more details. /// The placeholder type may be omitted and determined from the placeholder name, if the name has one of the following prefixes: /// /// expr, expression - expression placeholder, e.g. $exprPlaceholder{}$, $expressionFoo{}$ /// arg, argument - argument placeholder, e.g. $argPlaceholder{}$, $argumentFoo{}$ /// ident, identifier - identifier placeholder, e.g. $identPlaceholder{}$, $identifierFoo{}$ /// stmt, statement - statement placeholder, e.g. $stmtPlaceholder{}$, $statementFoo{}$ /// type - type placeholder, e.g. $typePlaceholder{}$, $typeFoo{}$ /// member - member placeholder, e.g. $memberPlaceholder{}$, $memberFoo{}$ /// /// /// /// Expression placeholder arguments: /// /// expressionType - string value in single quotes, specifies full type name to match (empty string by default) /// exactType - boolean value, specifies if expression should have exact type match (false by default) /// /// Examples: /// /// $myExpr{Expression, 'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. /// $myExpr{Expression, 'Namespace.FooType'}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type or expressions which can be implicitly converted to 'Namespace.FooType'. /// $myExpr{Expression}$ - defines expression placeholder, matching expressions of any type. /// $exprFoo{'Namespace.FooType', true}$ - defines expression placeholder, matching expressions of the 'Namespace.FooType' type with exact matching. /// /// /// /// Type placeholder arguments: /// /// type - string value in single quotes, specifies full type name to match (empty string by default) /// exactType - boolean value, specifies if expression should have exact type match (false by default) /// /// Examples: /// /// $myType{Type, 'Namespace.FooType', true}$ - defines type placeholder, matching 'Namespace.FooType' types with exact matching. /// $myType{Type, 'Namespace.FooType'}$ - defines type placeholder, matching 'Namespace.FooType' types or types, which can be implicitly converted to 'Namespace.FooType'. /// $myType{Type}$ - defines type placeholder, matching any type. /// $typeFoo{'Namespace.FooType', true}$ - defines types placeholder, matching 'Namespace.FooType' types with exact matching. /// /// /// /// Identifier placeholder arguments: /// /// nameRegex - string value in single quotes, specifies regex to use for matching (empty string by default) /// nameRegexCaseSensitive - boolean value, specifies if name regex is case sensitive (true by default) /// type - string value in single quotes, specifies full type name to match (empty string by default) /// exactType - boolean value, specifies if expression should have exact type match (false by default) /// /// Examples: /// /// $myIdentifier{Identifier, 'my.*', false, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (ignoring case) starting with 'my' prefix with 'Namespace.FooType' type. /// $myIdentifier{Identifier, 'my.*', true, 'Namespace.FooType', true}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix with 'Namespace.FooType' type. /// $identFoo{'my.*'}$ - defines identifier placeholder, matching identifiers (case sensitively) starting with 'my' prefix. /// /// /// /// Statement placeholder arguments: /// /// minimalOccurrences - minimal number of statements to match (-1 by default) /// maximalOccurrences - maximal number of statements to match (-1 by default) /// /// Examples: /// /// $myStmt{Statement, 1, 2}$ - defines statement placeholder, matching 1 or 2 statements. /// $myStmt{Statement}$ - defines statement placeholder, matching any number of statements. /// $stmtFoo{1, 2}$ - defines statement placeholder, matching 1 or 2 statements. /// /// /// /// Argument placeholder arguments: /// /// minimalOccurrences - minimal number of arguments to match (-1 by default) /// maximalOccurrences - maximal number of arguments to match (-1 by default) /// /// Examples: /// /// $myArg{Argument, 1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. /// $myArg{Argument}$ - defines argument placeholder, matching any number of arguments. /// $argFoo{1, 2}$ - defines argument placeholder, matching 1 or 2 arguments. /// /// /// /// Member placeholder arguments: /// /// docId - string value in single quotes, specifies XML documentation id of the member to match (empty by default) /// /// Examples: /// /// $myMember{Member, 'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. /// $memberFoo{'M:System.String.IsNullOrEmpty(System.String)'}$ - defines member placeholder, matching 'IsNullOrEmpty' member of the 'System.String' type. /// /// /// /// For more information please refer to the Structural Search and Replace article. /// /// [AttributeUsage( AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)] internal sealed class CodeTemplateAttribute : Attribute { public CodeTemplateAttribute(string searchTemplate) { SearchTemplate = searchTemplate; } /// /// Structural search pattern to use in the code template. /// The pattern includes a textual part, which must contain only identifiers allowed in the target language, /// and placeholders, which allow matching variable parts of the target code blocks. /// public string SearchTemplate { get; } /// /// Message to show when the search pattern was found. /// You can also prepend the message text with "Error:", "Warning:", "Suggestion:" or "Hint:" prefix to specify the pattern severity. /// Code patterns with replace templates produce suggestions by default. /// However, if a replace template is not provided, then warning severity will be used. /// public string? Message { get; set; } /// /// Structural search replace pattern to use in code template replacement. /// public string? ReplaceTemplate { get; set; } /// /// The replace message to show in the light bulb. /// public string? ReplaceMessage { get; set; } /// /// Apply code formatting after code replacement. /// public bool FormatAfterReplace { get; set; } = true; /// /// Whether similar code blocks should be matched. /// public bool MatchSimilarConstructs { get; set; } /// /// Automatically insert namespace import directives or remove qualifiers that become redundant after the template is applied. /// public bool ShortenReferences { get; set; } /// /// The string to use as a suppression key. /// By default the following suppression key is used 'CodeTemplate_SomeType_SomeMember', /// where 'SomeType' and 'SomeMember' are names of the associated containing type and member to which this attribute is applied. /// public string? SuppressionKey { get; set; } } }