Пользовательские предупреждения компилятора

При использовании ObsoleteAtribute в .Net он дает вам предупреждения о компиляторе, говорящие вам, что объект/метод/свойство устарели, а что-то еще нужно использовать. В настоящее время я работаю над проектом, который требует много рефакторинга кода бывших сотрудников. Я хочу написать пользовательский атрибут, который я могу использовать для обозначения методов или свойств, которые будут генерировать предупреждения компилятора, которые дают сообщения, которые я пишу. Что-то вроде этого

[MyAttribute("This code sux and should be looked at")]
public sub DoEverything(){}

Я хочу, чтобы это генерировало предупреждение о компиляторе, в котором говорится: "Этот код sux и его нужно посмотреть". Я знаю, как создать пользовательский атрибут, вопрос в том, как заставить его генерировать предупреждения компилятора в visual studio.

Ответ 1

Update

Теперь это возможно с Roslyn (Visual Studio 2015). Вы можете построить анализатор кода, чтобы проверить пользовательский атрибут


Я не думаю, что это возможно. ObsoleteAttribute обрабатывается специально компилятором и определяется в стандарте С#. Почему же ObsoleteAttribute неприемлем? Мне кажется, что это именно та ситуация, в которой она была разработана, и обеспечивает именно то, что вам нужно!

Также обратите внимание, что Visual Studio автоматически подбирает предупреждения, генерируемые ObsoleteAttribute, что очень полезно.

Не значит быть бесполезным, просто интересно, почему вы не любите его использовать...

К сожалению, ObsoleteAttribute запечатан (возможно, частично из-за специального лечения), поэтому вы не можете подклассифицировать свой собственный атрибут.

Из стандарта С#: -

Атрибут Устаревший используется для обозначения типы и типы типов, которые должны больше не используются.

Если программа использует тип или элемент который украшен Устаревшим атрибут, компилятор выдает предупреждения или ошибки. В частности, компилятор выдает предупреждение, если нет ошибки параметр, или если ошибка параметр предоставляется и имеет значение false. Компилятор выдает если параметр ошибки и имеет значение true.

Разве это не суммирует ваши потребности?... вы не будете делать лучше, чем я не думаю.

Ответ 2

Не знаю, будет ли это работать, но стоит попробовать.

Вы не можете распространять Obsolete, потому что его окончательный, но, возможно, вы можете создать свой собственный атрибут и пометить этот класс как устаревший:

[Obsolete("Should be refactored")]
public class MustRefactor: System.Attribute{}

Затем, когда вы помечаете свои методы атрибутом "MustRefactor", могут отображаться предупреждения компиляции.

Я сказал "возможно" и "могу", потому что я этого не пробовал. Скажите, пожалуйста, если он не работает, поэтому я удалю ответ.

Привет!

ОБНОВЛЕНИЕ: Протестировано. Он генерирует предупреждение о времени компиляции, но сообщение об ошибке выглядит смешно, вы должны увидеть его сами и выбрать. Это очень близко к тому, чего вы хотели достичь.

UPDATE2: С этот код Он генерирует эти предупреждения (не очень приятно, но я не думаю, что там что-то лучше).

public class User
{
    private String userName;

    [TooManyArgs] // Will show warning: Try removing some arguments
    public User(String userName)
    {
        this.userName = userName;   
    }

    public String UserName
    {
        get { return userName; }
    }
    [MustRefactor] // will show warning: Refactor is needed Here
    public override string ToString()
    {
        return "User: " + userName;
    }
}
[Obsolete("Refactor is needed Here")]
public class MustRefactor : System.Attribute
{

}
[Obsolete("Try removing some arguments")]
public class TooManyArgs : System.Attribute
{

}

Ответ 3

В некоторых компиляторах вы можете использовать #warning для выдачи предупреждения:

#warning "Do not use ABC, which is deprecated. Use XYZ instead."

В компиляторах Microsoft вы обычно можете использовать сообщение pragma:

#pragma message ( "text" )

Вы упомянули .Net, но не указали, программируете ли вы с C/С++ или С#. Если вы программируете на С#, вы должны знать, что С# поддерживает формат #warning.

Ответ 4

В настоящее время мы находимся в центре рефакторинга, где мы не можем все сразу исправить. Мы просто используем команду #warning preproc, где нам нужно вернуться и посмотреть на код. Он отображается в выводе компилятора. Я не думаю, что вы можете применить его к методу, но вы можете использовать его только внутри метода, и его все еще легко найти.

public void DoEverything() {
   #warning "This code sucks"
}

Ответ 5

В VS 2008 (+ sp1) #warnings не отображаются должным образом в списке ошибок после Clean Soultion и Rebuild Solution, не все из них. Некоторые предупреждения отображаются в списке ошибок только после того, как я открою конкретный файл класса. Поэтому я был вынужден использовать пользовательский атрибут:

[Obsolete("Mapping ToDo")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class MappingToDo : System.Attribute
{
    public string Comment = "";

    public MappingToDo(string comment)
    {
        Comment = comment;
    }

    public MappingToDo()
    {}
}

Итак, когда я флаг с ним

[MappingToDo("Some comment")]
public class MembershipHour : Entity
{
    // .....
}

Он выдает такие предупреждения:

Пространство имен .MappingToDo устарело: 'Отображение ToDo'.

Я не могу изменить текст предупреждения, "Некоторый комментарий" не показывает его список ошибок. Но он переместится в нужное место в файле. Поэтому, если вам необходимо изменить такие предупреждающие сообщения, создайте различные атрибуты.

Ответ 6

То, что вы пытаетесь сделать, - это неправильное использование атрибутов. Вместо этого используйте Список задач Visual Studio. Вы можете ввести комментарий в свой код следующим образом:

//TODO:  This code sux and should be looked at
public class SuckyClass(){
  //TODO:  Do something really sucky here!
}

Затем откройте меню View/Task List. Список задач имеет две категории: пользовательские задачи и комментарии. Переключитесь на комментарии, и вы увидите все ваши //Todo: там. Двойной щелчок на TODO переместится к комментарию в вашем коде.

Ал

Ответ 7

Я не думаю, что ты можешь. Насколько я знаю, поддержка ObsoleteAttribute по существу жестко закодирована в компиляторе С#; вы не можете делать ничего подобного напрямую.

Что вы можете сделать, так это использовать задачу MSBuild (или событие после сборки), которая выполняет собственный инструмент против только что скомпилированной сборки. Пользовательский инструмент будет отражать все типы/методы в сборке и потреблять ваш пользовательский атрибут, после чего он может печатать на System.Console по умолчанию или с ошибкой TextWriters.

Ответ 8

Глядя на источник ObsoleteAttribute, похоже, что он ничего не делает для генерации предупреждения компилятора, поэтому я будет иметь тенденцию идти с @technophile и сказать, что он жестко закодирован в компилятор. Есть ли причина, по которой вы не хотите просто использовать ObsoleteAttribute для создания предупреждающих сообщений?

Ответ 9

Есть несколько комментариев, которые предлагают вставлять предупреждения или прагма. Устаревшие работы совершенно по-другому! Отмечая устаревшую функцию библиотеки L, устаревшее сообщение возникает, когда программа вызывает эту функцию, даже если программа вызывающего абонента не находится в библиотеке L. Предупреждение поднимает сообщение ТОЛЬКО при компиляции L.

Ответ 10

Вот реализация Roslyn, поэтому вы можете создать свои собственные атрибуты, которые дают предупреждения или ошибки мухи.

Я создал атрибут Type Called IdeMessage, который будет атрибутом, который генерирует предупреждения:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class IDEMessageAttribute : Attribute
{
    public string Message;

    public IDEMessageAttribute(string message);
}

Для этого вам нужно сначала установить Roslyn SDK и начать новый проект VSIX с помощью анализатора, я опустил часть менее значимой части, например сообщения, которые вы можете выяснить, как это сделать, в вашем анализаторе вы делаете это

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzerInvocation, SyntaxKind.InvocationExpression);
}

private static void AnalyzerInvocation(SyntaxNodeAnalysisContext context)
{
    var invocation = (InvocationExpressionSyntax)context.Node;

    var methodDeclaration = (context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol as IMethodSymbol);

    //There are several reason why this may be null e.g invoking a delegate
    if (null == methodDeclaration)
    {
        return;
    }

    var methodAttributes = methodDeclaration.GetAttributes();
    var attributeData = methodAttributes.FirstOrDefault(attr => IsIDEMessageAttribute(context.SemanticModel, attr, typeof(IDEMessageAttribute)));
    if(null == attributeData)
    {
        return;
    }

    var message = GetMessage(attributeData); 
    var diagnostic = Diagnostic.Create(Rule, invocation.GetLocation(), methodDeclaration.Name, message);
    context.ReportDiagnostic(diagnostic);
}

static bool IsIDEMessageAttribute(SemanticModel semanticModel, AttributeData attribute, Type desiredAttributeType)
{
    var desiredTypeNamedSymbol = semanticModel.Compilation.GetTypeByMetadataName(desiredAttributeType.FullName);

    var result = attribute.AttributeClass.Equals(desiredTypeNamedSymbol);
    return result;
}

static string GetMessage(AttributeData attribute)
{
    if (attribute.ConstructorArguments.Length < 1)
    {
        return "This method is obsolete";
    }

    return (attribute.ConstructorArguments[0].Value as string);
}

Для этого нет CodeFixProvider, вы можете удалить его из решения.