Benutzerdefinierte Compiler-Warnungen

115

Wenn Sie das ObsoleteAtribute in .Net verwenden, erhalten Sie Compiler-Warnungen, die Sie darüber informieren, dass das Objekt / die Methode / die Eigenschaft veraltet ist und etwas anderes verwendet werden sollte. Ich arbeite derzeit an einem Projekt, bei dem ein Code für ehemalige Mitarbeiter viel überarbeitet werden muss. Ich möchte ein benutzerdefiniertes Attribut schreiben, mit dem ich Methoden oder Eigenschaften markieren kann, die Compiler-Warnungen generieren, die von mir geschriebene Nachrichten enthalten. Etwas wie das

[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

Ich möchte, dass dies eine Compiler-Warnung generiert, die besagt: "Dieser Code ist sux und sollte betrachtet werden". Ich weiß, wie man ein benutzerdefiniertes Attribut erstellt. Die Frage ist, wie ich es veranlassen kann, Compiler-Warnungen in Visual Studio zu generieren.

Micah
quelle
Ist das C #? Ich werde dies vermutlich als C # (nicht C) wiederholen, vorausgesetzt, das Originalplakat wollte es auswählen.
Onorio Catenacci
13
Das ist nicht gültig VB oder C # ... also was ist es ...?!
ljs
5
Alte Frage, aber Sie können jetzt mit Roslyn benutzerdefinierte Compiler-Warnungen definieren.
RJ Cuthbertson
4
@jrummell In Roslyn sprechen Code-Analysatoren: johnkoerner.com/csharp/creating-your-first-code-analyzer
RJ Cuthbertson
2
@RJCuthbertson Ich habe Ihren Kommentar in die akzeptierte Antwort verschoben, um ihm die Aufmerksamkeit zu schenken, die er verdient.
Jpaugh

Antworten:

27

Aktualisieren

Dies ist jetzt mit Roslyn (Visual Studio 2015) möglich. Sie können bauen einen Code Analyzer für ein benutzerdefiniertes Attribut zu überprüfen


Ich glaube nicht, dass es möglich ist. ObsoleteAttribute wird vom Compiler speziell behandelt und im C # -Standard definiert. Warum um alles in der Welt ist ObsoleteAttribute nicht akzeptabel? Es scheint mir, dass dies genau die Situation ist, für die es entwickelt wurde, und genau das erreicht, was Sie benötigen!

Beachten Sie auch, dass Visual Studio die von ObsoleteAttribute generierten Warnungen auch im laufenden Betrieb aufnimmt, was sehr nützlich ist.

Ich will nicht wenig hilfreich sein und mich nur fragen, warum Sie es nicht gerne benutzen ...

Leider ist ObsoleteAttribute versiegelt (wahrscheinlich teilweise aufgrund der Sonderbehandlung), daher können Sie Ihr eigenes Attribut nicht daraus unterordnen.

Aus dem C # -Standard: -

Das Attribut Veraltet wird verwendet, um Typen und Mitglieder von Typen zu markieren, die nicht mehr verwendet werden sollen.

Wenn ein Programm einen Typ oder ein Element verwendet, das mit dem Attribut "Veraltet" versehen ist, gibt der Compiler eine Warnung oder einen Fehler aus. Insbesondere gibt der Compiler eine Warnung aus, wenn kein Fehlerparameter angegeben ist oder wenn der Fehlerparameter angegeben ist und den Wert false hat. Der Compiler gibt einen Fehler aus, wenn der Fehlerparameter angegeben ist und den Wert true hat.

Fasst das nicht Ihre Bedürfnisse zusammen? ... Sie werden es nicht besser machen, denke ich nicht.

ljs
quelle
14
Ich suche das Gleiche. Veraltet 'funktioniert', aber der Code ist aufgrund von Refactoring nicht wirklich veraltet, sondern unvollständig.
g.
11
Ich stimme @g und wahrscheinlich dem ursprünglichen Autor zu. Veraltet bedeutet veraltet, nicht verwenden. Ich möchte etwas als "hey das kompiliert, aber wir müssen wirklich entweder a) die Funktionalität vervollständigen oder b) refactor" kennzeichnen. Es wäre eher ein Entwicklungszeitattribut. Auch Aufgaben funktionieren, z. B. // TODO:, aber ich verwende diese nicht, da ich vermute, dass viele Leute dies nicht tun, sondern die Compiler-Warnungen regelmäßig überprüfen.
MikeJansen
8
Ein weiterer Grund, das [Obsolete]Tag nicht zu verwenden , kann Probleme verursachen, wenn Sie XmlSerialization mit der Eigenschaft durchführen müssen. Durch Hinzufügen des [Obsolete]Tags wird auch ein [XmlIgnore]Attribut hinter den Kulissen hinzugefügt.
Burnttoast11
6
Veraltet ist anders. Veraltet gibt in jeder Codezeile, die diese Methode aufruft, eine Warnung aus. Ich glaube nicht, dass das Poster das will (zumindest will ich das nicht, als ich eine Suche durchgeführt und diese Frage gefunden habe). Ich dachte, die Frage suchte nach einer Warnung, die in der Definition der Funktion angezeigt werden sollte, und nicht nach dem Ort, an dem sie verwendet wird.
Nick
Nicht die beste Antwort. -1 für den Gedanken, dass Ihre Unfähigkeit, einen Grund für die Nichtverwendung zu finden, Kritik verdient. Diese Haltung entmutigt die Authentizität.
Mike Socha III
96

Das ist einen Versuch wert.

Sie können Obsolete nicht erweitern, da es endgültig ist, aber vielleicht können Sie Ihr eigenes Attribut erstellen und diese Klasse wie folgt als veraltet markieren:

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

Wenn Sie dann Ihre Methoden mit dem Attribut "MustRefactor" markieren, werden die Kompilierungswarnungen angezeigt. Es wird eine Warnung zur Kompilierungszeit generiert, aber die Fehlermeldung sieht lustig aus. Sie sollten sie selbst sehen und auswählen. Dies kommt dem sehr nahe, was Sie erreichen wollten.

UPDATE: Mit diesem Code wird eine Warnung generiert (nicht sehr schön, aber ich denke nicht, dass es etwas Besseres gibt).

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
{

}
Pablo Fernandez
quelle
Können Sie einfügen, was es erzeugt? Ich bin neugierig.
Micah
1
Die Kompilierungswarnung wird auch dann ausgelöst, wenn die Eigenschaft / Methode nicht aufgerufen wird.
Rolf Kristensen
1
Gute Vorschläge hier. Ich wollte das Gleiche tun und warf am Ende nur NotImplementedExceptions. Nicht die beste Lösung, da sie nicht zur Kompilierungszeit angezeigt werden, sondern nur zur Laufzeit, wenn der Code gerade ausgeführt wird. Ich werde es selbst versuchen.
MonkeyWrench
1
Wäre es nicht großartig, wenn das ObsolteAttribute Ausdrücke wie DebuggerDisplayAttribute unterstützen könnte, dann könnten wir wirklich ein paar coole Sachen machen. visualstudio.uservoice.com/forums/121579-visual-studio/…
jpierson
Wenn Sie IDisposablediese veralteten Klassen implementieren , bedeutet dies, dass Sie Ihren zwielichtigen Testcode in einen usingBlock einschließen können. So : using(new MustRefactor()){DodgyCode();}. Dann können Sie alle Verwendungen finden, wenn Sie fertig sind. Ich verwende dies gerade für Sleepden Thread in einer for-Schleife, die ich für Debugging-Zwecke künstlich verlangsamen muss.
Iain Fraser
48

In einigen Compilern können Sie #warning verwenden, um eine Warnung auszugeben:

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

In Microsoft-Compilern können Sie normalerweise das Nachrichtenpragma verwenden:

#pragma message ( "text" )

Sie haben .Net erwähnt, aber nicht angegeben, ob Sie mit C / C ++ oder C # programmieren. Wenn Sie in C # programmieren, sollten Sie wissen, dass C # das # warning-Format unterstützt .

Douglas Mayle
quelle
1
#warning oder #pragma sind Pre-Prozessor-Direktiven und werden daher unabhängig vom Vorhandensein des Codes von Ex-Kollegen von micah ausgeführt und interagieren überhaupt nicht mit dem Attribut. Ziemlich sicher, dass Veraltet das einzige Mittel ist, um dies zu erreichen ...
ljs
39

Wir befinden uns derzeit mitten in einer Umgestaltung, bei der wir nicht alles sofort reparieren konnten. Wir verwenden nur den Befehl #warning preproc, wo wir zurückgehen und uns den Code ansehen müssen. Es wird in der Compiler-Ausgabe angezeigt. Ich glaube nicht, dass Sie es auf eine Methode setzen können, aber Sie könnten es einfach in die Methode einfügen, und es ist immer noch leicht zu finden.

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

In VS 2008 (+ sp1) werden #Warnungen in der Fehlerliste nach Clean Soultion & Rebuild Solution nicht richtig angezeigt, nicht alle. Einige Warnungen werden in der Fehlerliste erst angezeigt, nachdem ich eine bestimmte Klassendatei geöffnet habe. Also musste ich ein benutzerdefiniertes Attribut verwenden:

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

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

    public MappingToDo()
    {}
}

Also, wenn ich einen Code damit kennzeichne

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

Es werden Warnungen wie diese ausgegeben:

Namespace.MappingToDo ist veraltet: 'Mapping ToDo'.

Ich kann den Text der Warnung nicht ändern. 'Einige Kommentare' werden nicht angezeigt. Fehlerliste. Aber es wird an die richtige Stelle in der Datei springen. Wenn Sie solche Warnmeldungen ändern müssen, erstellen Sie verschiedene Attribute.

Tomasz Modelski
quelle
6

Was Sie versuchen, ist ein Missbrauch von Attributen. Verwenden Sie stattdessen die Visual Studio-Aufgabenliste. Sie können einen Kommentar wie folgt in Ihren Code eingeben:

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

Öffnen Sie dann Ansicht / Aufgabenliste aus dem Menü. Die Aufgabenliste enthält zwei Kategorien: Benutzeraufgaben und Kommentare. Wechseln Sie zu Kommentare und Sie sehen alle Ihre // Todo: 's dort. Ein Doppelklick auf ein TODO springt zum Kommentar in Ihrem Code.

Al

user4089256
quelle
1
Ich finde das eine vorzuziehende Lösung
Samuel
1
Was ist, wenn Sie eine Funktion als "Nicht im Produktionscode aufzurufen" oder ähnliches markieren möchten. Sie möchten also, dass es ausgelöst wird, wenn eine Funktion oder Klasse aufgerufen oder instanziiert wird, aber nicht, wenn sie nur kompiliert wird.
Jesse Pepper
2

Ich glaube nicht, dass du kannst. Soweit ich weiß, ist die Unterstützung für ObsoleteAttribute im Wesentlichen im C # -Compiler fest codiert. Sie können nichts Ähnliches direkt tun.

Möglicherweise können Sie eine MSBuild-Task (oder ein Post-Build-Ereignis) verwenden, mit der ein benutzerdefiniertes Tool für die gerade kompilierte Assembly ausgeführt wird. Das benutzerdefinierte Tool spiegelt alle Typen / Methoden in der Assembly wider und verwendet Ihr benutzerdefiniertes Attribut. Zu diesem Zeitpunkt kann es in den Standard- oder FehlertextWriters von System.Console gedruckt werden.

technophil
quelle
2

Wenn man sich die Quelle für ObsoleteAttribute ansieht , sieht es nicht so aus, als würde es etwas Besonderes tun, um eine Compiler-Warnung zu generieren. Daher würde ich eher mit @ technophile arbeiten und sagen, dass es im Compiler fest codiert ist. Gibt es einen Grund, warum Sie ObsoleteAttribute nicht nur zum Generieren Ihrer Warnmeldungen verwenden möchten ?

bdukes
quelle
Kein anderer Grund als Code ist nicht unbedingt veraltet.
Micah
1
Es ist in der C # -Spezifikation angegeben, dass es vom Compiler speziell behandelt wird. Schauen Sie sich meine Antwort an :-). Micah - 'Das Attribut Veraltet wird verwendet, um Typen und Mitglieder von Typen zu markieren, die nicht mehr verwendet werden sollen.' aus der Spezifikation. Ist das nicht zutreffend? ...
ljs
Nur wenn sich jemand gefragt hat, gibt es auch keinen C # -Code im Quellcode, um dies zu tun. referencesource.microsoft.com/#mscorlib/system/…
Paweł Mach
1

Es gibt mehrere Kommentare, die darauf hinweisen, Warnungen oder Pragma einzufügen. Veraltet funktioniert ganz anders! Wenn eine Funktion einer Bibliothek L als veraltet markiert wird, wird die veraltete Nachricht ausgelöst, wenn ein Programm die Funktion aufruft, auch wenn sich das aufrufende Programm nicht in der Bibliothek L befindet. Warnung löst die Nachricht NUR aus, wenn L kompiliert wird.

Bubi
quelle
1

Hier ist die Roslyn-Implementierung, mit der Sie Ihre eigenen Attribute erstellen können, die im laufenden Betrieb Warnungen oder Fehler ausgeben.

Ich habe einen Attributtyp namens Called erstellt, IdeMessageder das Attribut ist, das Warnungen generiert:

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

    public IDEMessageAttribute(string message);
}

Dazu müssen Sie zuerst das Roslyn SDK installieren und ein neues VSIX-Projekt mit Analyzer starten. Ich habe einige der weniger relevanten Teile wie die Nachrichten weggelassen. Sie können herausfinden, wie das geht. In Ihrem Analysegerät tun Sie dies

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

Es gibt keinen CodeFixProvider dafür, den Sie aus der Lösung entfernen können.

Johnny 5
quelle