web-dev-qa-db-fra.com

Avertissements du compilateur personnalisé

Lorsque vous utilisez ObsoleteAtribute dans .Net, il vous donne des avertissements du compilateur vous indiquant que l'objet/méthode/propriété est obsolète et que quelque chose d'autre doit être utilisé. Je travaille actuellement sur un projet qui nécessite beaucoup de refactoring d'un code d'anciens employés. Je veux écrire un attribut personnalisé que je peux utiliser pour marquer des méthodes ou des propriétés qui généreront des avertissements du compilateur qui donneront des messages que j'écris. Quelque chose comme ça

[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

Je veux que cela génère un avertissement du compilateur qui dit: "Ce code sux et doit être consulté". Je sais comment créer un attribut personnalisé, la question est de savoir comment le faire générer des avertissements du compilateur dans Visual Studio.

110
Micah

Mise à jour

Cela est désormais possible avec Roslyn (Visual Studio 2015). Vous pouvez construire a analyseur de code pour vérifier un attribut personnalisé


Je ne pense pas que ce soit possible. ObsoleteAttribute est traité spécialement par le compilateur et est défini dans la norme C #. Pourquoi diable ObsoleteAttribute n'est-il pas acceptable? Il me semble que c'est précisément la situation pour laquelle il a été conçu et atteint précisément ce dont vous avez besoin!

Notez également que Visual Studio récupère également les avertissements générés par ObsoleteAttribute à la volée, ce qui est très utile.

Je ne veux pas être inutile, je me demande simplement pourquoi vous ne souhaitez pas l'utiliser ...

Malheureusement, ObsoleteAttribute est scellé (probablement en partie à cause du traitement spécial), vous ne pouvez donc pas en sous-classer votre propre attribut.

De la norme C #: -

L'attribut Obsolète est utilisé pour marquer les types et les membres de types qui ne devraient plus être utilisés.

Si un programme utilise un type ou un membre décoré avec l'attribut obsolète, le compilateur émet un avertissement ou une erreur. Plus précisément, le compilateur émet un avertissement si aucun paramètre d'erreur n'est fourni ou si le paramètre d'erreur est fourni et a la valeur false. Le compilateur émet une erreur si le paramètre d'erreur est spécifié et a la valeur true.

Cela ne résume-t-il pas vos besoins? ... vous n'allez pas faire mieux que cela, je ne pense pas.

25
ljs

Je ne sais pas si cela fonctionnera mais ça vaut le coup d'essayer.

Vous ne pouvez pas étendre Obsolète, car c'est définitif, mais peut-être pouvez-vous créer votre propre attribut et marquer cette classe comme obsolète comme ceci:

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

Ensuite, lorsque vous marquez vos méthodes avec l'attribut "MustRefactor", les avertissements de compilation peuvent s'afficher.

J'ai dit "peut-être" et "pourrait" parce que je n'ai pas essayé cela. Veuillez me dire si cela ne fonctionne pas, je supprimerai donc la réponse.

Cordialement!

MISE À JOUR: Testé. Il génère un avertissement de temps de compilation, mais le message d'erreur a l'air drôle, vous devriez le voir par vous-même et choisir. C'est très proche de ce que vous vouliez réaliser.

UPDATE2: Avec ce code Il génère cet avertissement (pas très bien, mais je ne pense pas qu'il y ait quelque chose de mieux).

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
{

}
93
Pablo Fernandez

Dans certains compilateurs, vous pouvez utiliser #warning pour émettre un avertissement:

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

Dans les compilateurs Microsoft, vous pouvez généralement utiliser le pragma de message:

#pragma message ( "text" )

Vous avez mentionné .Net, mais n'avez pas précisé si vous programmiez avec C/C++ ou C #. Si vous programmez en C #, vous devez savoir que C # prend en charge le format #warning .

44
Douglas Mayle

Nous sommes actuellement en plein remaniement où nous n'avons pas pu tout réparer tout de suite. Nous utilisons simplement la commande #warning preproc où nous devons revenir en arrière et regarder le code. Il apparaît dans la sortie du compilateur. Je ne pense pas que vous puissiez le mettre sur une méthode, mais vous pouvez le mettre juste à l'intérieur de la méthode, et c'est toujours facile à trouver.

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

Dans VS 2008 (+ sp1), les avertissements ne s'affichent pas correctement dans la liste d'erreurs après la solution Clean Soultion & Rebuild, pas tous. Certains avertissements sont affichés dans la liste d'erreurs uniquement après avoir ouvert un fichier de classe particulier. J'ai donc été obligé d'utiliser un attribut personnalisé:

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

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

    public MappingToDo()
    {}
}

Donc, quand je signale du code avec

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

Il génère des avertissements comme celui-ci:

Namespace.MappingToDo est obsolète: "Mapping ToDo".

Je ne peux pas modifier le texte de l'avertissement, "Certains commentaires" ne s'affiche pas dans la liste des erreurs. Mais il sautera au bon endroit dans le fichier. Donc, si vous devez faire varier ces messages d'avertissement, créez divers attributs.

7
Tomasz Modelski

Ce que vous essayez de faire, c'est une mauvaise utilisation des attributs. Utilisez plutôt la liste des tâches de Visual Studio. Vous pouvez entrer un commentaire dans votre code comme ceci:

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

Ouvrez ensuite Afficher/Liste des tâches dans le menu. La liste des tâches comprend deux catégories, les tâches utilisateur et les commentaires. Passez aux commentaires et vous verrez tous vos // Todo: là-bas. Double-cliquez sur un TODO pour accéder au commentaire dans votre code.

Al

6
user4089256

Je ne pense pas que vous puissiez. Pour autant que je sache, la prise en charge d'ObsoleteAttribute est essentiellement codée en dur dans le compilateur C #; vous ne pouvez pas faire quelque chose de similaire directement.

Ce que vous pourriez faire est d'utiliser une tâche MSBuild (ou un événement post-build) qui exécute un outil personnalisé sur l'assembly qui vient d'être compilé. L'outil personnalisé se refléterait sur tous les types/méthodes de l'assembly et consommerait votre attribut personnalisé, auquel cas il pourrait imprimer sur TextWriters par défaut ou d'erreur de System.Console.

2
technophile

En regardant la source de ObsoleteAttribute , il ne semble pas que cela fasse quelque chose de spécial pour générer un avertissement du compilateur, donc j'aurais tendance à aller avec @ technophile et à dire qu'il est codé en dur dans le compilateur. Y a-t-il une raison pour laquelle vous ne voulez pas simplement utiliser ObsoleteAttribute pour générer vos messages d'avertissement?

2
bdukes

Il y a plusieurs commentaires qui suggèrent d'insérer des avertissements ou du pragma. Obsolète fonctionne d'une manière très différente! Marquant une fonction obsolète d'une bibliothèque L, le message obsolète se déclenche lorsqu'un programme appelle la fonction même si le programme appelant n'est pas dans la bibliothèque L. L'avertissement ne déclenche le message QUE lorsque L est compilé.

1
bubi

Voici l'implémentation de Roslyn, vous pouvez donc créer vos propres attributs qui donnent des avertissements ou des erreurs à la volée.

J'ai créé un attribut de type appelé IdeMessage qui sera l'attribut qui génère des avertissements:

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

    public IDEMessageAttribute(string message);
}

Pour ce faire, vous devez d'abord installer le SDK Roslyn et démarrer un nouveau projet VSIX avec analyseur. J'ai omis certains des éléments les moins pertinents comme les messages, vous pouvez comprendre comment le faire. Dans votre analyseur, vous faites cela

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);
}

Il n'y a pas de CodeFixProvider pour cela, vous pouvez le supprimer de la solution.

1
johnny 5