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

115

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

[MyAttribute("This code sux and should be looked at")]
public void DoEverything()
{
}
<MyAttribute("This code sux and should be looked at")>
Public Sub DoEverything()
End Sub

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

Михей
источник
Это C #? Я собираюсь предположительно пометить это как C # (не C), исходя из предположения, что исходный плакат хотел выбрать именно это.
Онорио Катеначчи,
13
Это недопустимый VB или C # ... так что это ...?!
ljs
5
Старый вопрос, но теперь вы можете определить пользовательские предупреждения компилятора с помощью Roslyn.
RJ Cuthbertson
4
@jrummell Говоря Roslyn, анализаторы кода: johnkoerner.com/csharp/creating-your-first-code-analyzer
RJ Cuthbertson
2
@RJCuthbertson Я переместил ваш комментарий в принятый ответ, чтобы уделить ему должное внимание.
jpaugh

Ответы:

27

Обновить

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


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

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

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

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

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

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

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

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

СМЛ
источник
14
Я ищу то же самое. Устаревшее «работает», но код на самом деле не столько устаревший, сколько неполный из-за рефакторинга.
г.
11
Я согласен с @g и, вероятно, с первоначальным автором. Устаревшее означает устаревшее, не используйте. Я хочу отметить что-то как «привет, это компилируется, но нам действительно нужно либо а) завершить функциональность, либо б) провести рефакторинг». Это было бы скорее атрибутом времени разработки. Также работают задачи, например // TODO :, но я не использую их, как я предполагаю, многие люди не используют, но регулярно просматриваю предупреждения компилятора.
MikeJansen
8
Еще одна причина не использовать [Obsolete]тег заключается в том, что это может вызвать проблемы, если вам нужно выполнить XmlSerialization со свойством. Добавление [Obsolete]тега также добавляет скрытый [XmlIgnore]атрибут.
burnttoast11
6
Устаревшее другое. Устаревший выдает предупреждение о каждой строке кода, вызывающей этот метод. Я не думаю, что это то, чего хочет плакат (по крайней мере, это не то, что я хотел, когда поискал и нашел этот вопрос). Я думал, что вопрос искал предупреждение, которое должно появиться в определении функции, а не в том месте, где оно используется.
Ник
Не лучший ответ. -1 за то, что считает, что ваша неспособность придумать причину, по которой не использовать его, заслуживает критики. Такое отношение препятствует подлинности.
Майк Соча III,
96

Это стоит попробовать.

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

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

Затем, когда вы помечаете свои методы атрибутом «MustRefactor», будут отображаться предупреждения компиляции. Он генерирует предупреждение во время компиляции, но сообщение об ошибке выглядит забавно, вы должны увидеть его сами и выбрать. Это очень близко к тому, чего вы хотели достичь.

ОБНОВЛЕНИЕ: с помощью этого кода генерируется предупреждение (не очень хорошо, но я не думаю, что есть что-то лучше).

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
{

}
Пабло Фернандес
источник
Вы можете вставить то, что он генерирует? Мне любопытно.
Мика
1
Предупреждение компиляции запускается, даже если свойство / метод не вызывается.
Рольф Кристенсен,
1
Здесь хорошие предложения. Я хотел сделать то же самое и в итоге просто выбросил NotImplementedExceptions. Не лучшее решение, поскольку они не отображаются во время компиляции, а только во время выполнения, если код выполняется. Я сам попробую.
MonkeyWrench 02
1
Было бы здорово, если бы ObsolteAttribute мог поддерживать такие выражения, как DebuggerDisplayAttribute, тогда мы действительно могли бы делать кое-что классное. visualstudio.uservoice.com/forums/121579-visual-studio/…
jpierson
Если вы реализуете IDisposableэти устаревшие классы, это означает, что вы можете заключить свой хитроумный тестовый код в usingблок. Как это: using(new MustRefactor()){DodgyCode();}. После этого вы сможете найти все способы использования. Я использую это прямо сейчас для Sleepпотока внутри цикла for, который мне нужно искусственно замедлить в целях отладки.
Iain Fraser
48

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

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

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

#pragma message ( "text" )

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

Дуглас Мэйл
источник
1
#warning или #pragma - это директивы препроцессора, поэтому они будут выполняться независимо от наличия кода бывших коллег Мики и вообще не взаимодействуют с атрибутом.
Совершенно очевидно, что
39

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

public void DoEverything() {
   #warning "This code sucks"
}
Тед Эллиотт
источник
7

В VS 2008 (+ sp1) # предупреждения не отображаются должным образом в списке ошибок после 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
{
    // .....
}

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

Namespace.MappingToDo устарело: «Mapping ToDo».

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

Томаш Моделски
источник
6

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

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

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

Al

user4089256
источник
1
Я считаю это более предпочтительным решением
Самуэль
1
что, если вы хотите пометить функцию как «Не должна вызываться в производственном коде» или подобное. Итак, вы хотите, чтобы он запускался, если функция или класс вызывается или создается, но не если он просто скомпилирован.
Джесси Пеппер
2

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

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

технофилов
источник
2

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

bdukes
источник
Никакая конкретная причина, кроме кода, не обязательно устарела.
Мика
1
Он указан в спецификации C # как специально обработанный компилятором, посмотрите мой ответ :-). Мика - «Атрибут Устаревший используется для обозначения типов и членов типов, которые больше не должны использоваться». из спецификации. Разве это не применимо? ...
ljs
На всякий случай, если кому-то интересно, в исходном коде нет кода C # для этого. linksource.microsoft.com/#mscorlib/system/…
Paweł Mach
1

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

Буби
источник
1

Вот реализация 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 для этого отсутствует, вы можете удалить его из решения.

Джонни 5
источник