2012-03-19 16 views
11

मेरे पास टूलकिट है जिसमें कई विधियां अक्सर Expression<Func<T,TProperty>> पैरामीटर के रूप में लेती हैं। कुछ केवल एकल-स्तर (o=>o.Name) हो सकते हैं, जबकि कुछ बहु-स्तर (o=>o.EmployeeData.Address.Street) हो सकते हैं।Roslyn एक संकलन-समय अभिव्यक्ति जांच के लिए सही उपकरण है?

मैं कुछ विकसित करना चाहता हूं (एमएसबिल्ड टास्क? विजुअल स्टूडियो प्लगइन? उम्मीद है कि पहले) जो सभी उपयोगकर्ता की .cs फ़ाइलों को पढ़ता है, और यदि दिया गया पैरामीटर संपत्ति-अभिव्यक्ति नहीं है (लेकिन o=>o.Contains("foo") जैसा कुछ है) , या यदि एक बहु-स्तरीय अभिव्यक्ति दी जाती है जहां केवल एक-स्तर की अनुमति है।

मैं संकलित आईएल कोड में पहले देख करने की कोशिश की लेकिन जब से अभिव्यक्ति पेड़ एक सी # संकलक "चाल" कर रहे हैं, आईएल में सब मैं देख अभिव्यक्ति उदाहरणों और इस तरह बनाने है, और जब तक मैं सकता है जांच प्रत्येक अगर केवल MemberExpressions (और उनमें से सही संख्या) बनाई गई है, यह बहुत अच्छा नहीं है।

फिर रोज़लिन मेरे दिमाग में आया। क्या रोज़लिन के साथ ऐसा कुछ लिखना संभव है?

+0

आपको इन बाधाओं को लागू करने की आवश्यकता क्यों है? –

+0

क्योंकि इन विधियों में मैं जो सामान करता हूं (संपत्ति परिवर्तन हैंडलिंग, त्रुटि जांच आदि) केवल संपत्ति अभिव्यक्ति – TDaver

+3

पर समझ में आता है और क्योंकि यह करने के लिए एक दिलचस्प चीज़ की तरह लग रहा है :) – TDaver

उत्तर

11

हाँ, मुझे लगता है कि रोज़लिन और इसके कोड के मुद्दे बिल्कुल सही उपकरण हैं। उनके साथ, आप टाइप करते समय कोड का विश्लेषण कर सकते हैं और त्रुटियों (या चेतावनियां) बना सकते हैं जो विजुअल स्टूडियो में अन्य त्रुटियों के रूप में दिखाए जाते हैं।

मैं ऐसे कोड मुद्दा बनाने की कोशिश की है:

[ExportSyntaxNodeCodeIssueProvider("PropertyExpressionCodeIssue", LanguageNames.CSharp, typeof(InvocationExpressionSyntax))] 
class PropertyExpressionCodeIssueProvider : ICodeIssueProvider 
{ 
    [ImportingConstructor] 
    public PropertyExpressionCodeIssueProvider() 
    {} 

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxNode node, CancellationToken cancellationToken) 
    { 
     var invocation = (InvocationExpressionSyntax)node; 

     var semanticModel = document.GetSemanticModel(cancellationToken); 

     var semanticInfo = semanticModel.GetSemanticInfo(invocation, cancellationToken); 

     var methodSymbol = (MethodSymbol)semanticInfo.Symbol; 

     if (methodSymbol == null) 
      yield break; 

     var attributes = methodSymbol.GetAttributes(); 

     if (!attributes.Any(a => a.AttributeClass.Name == "PropertyExpressionAttribute")) 
      yield break; 

     var arguments = invocation.ArgumentList.Arguments; 
     foreach (var argument in arguments) 
     { 
      var lambdaExpression = argument.Expression as SimpleLambdaExpressionSyntax; 
      if (lambdaExpression == null) 
       continue; 

      var parameter = lambdaExpression.Parameter; 
      var memberAccess = lambdaExpression.Body as MemberAccessExpressionSyntax; 
      if (memberAccess != null) 
      { 
       var objectIdentifierSyntax = memberAccess.Expression as IdentifierNameSyntax; 

       if (objectIdentifierSyntax != null 
        && objectIdentifierSyntax.PlainName == parameter.Identifier.ValueText 
        && semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol is PropertySymbol) 
        continue; 
      } 

      yield return 
       new CodeIssue(
        CodeIssue.Severity.Error, argument.Span, 
        string.Format("Has to be simple property access of '{0}'", parameter.Identifier.ValueText)); 
     } 
    } 

    #region Unimplemented ICodeIssueProvider members 

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxToken token, CancellationToken cancellationToken) 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerable<CodeIssue> GetIssues(IDocument document, CommonSyntaxTrivia trivia, CancellationToken cancellationToken) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion 
} 

उपयोग इस तरह होगा:

[AttributeUsage(AttributeTargets.Method)] 
class PropertyExpressionAttribute : Attribute 
{ } 

… 

[PropertyExpression] 
static void Foo<T>(Expression<Func<SomeType, T>> expr) 
{ } 

… 

Foo(x => x.P); // OK 
Foo(x => x.M()); // error 
Foo(x => 42); // error 

ऊपर कोड कई मुद्दों है:

  1. यह पूरी तरह से unoptimized है ।
  2. इसे शायद कुछ और त्रुटि जांच की आवश्यकता है।
  3. यह काम नहीं करता है। कम से कम वर्तमान सीटीपी में। अंत में semanticModel.GetSemanticInfo(memberAccess, cancellationToken).Symbol अभिव्यक्ति हमेशा null लौटाती है। ऐसा इसलिए है क्योंकि अभिव्यक्ति पेड़ के अर्थशास्त्र the currently unimplemented features के बीच है।
+3

क्या आपने अपना होमवर्क लिखा है? : डी – TDaver

+0

रोज़लिन एक नए सीटीपी के साथ बाहर आया। मुझे लगता है कि फिर कोशिश करने के लिए यह अच्छा समय होगा? :) – TDaver

5

हां, यह पूरी तरह से संभव है। समस्या यह है कि Roslyn अभी तक सभी भाषा संरचनाओं का समर्थन नहीं करता है, इसलिए आप कुछ असमर्थित सामग्री में भाग सकते हैं। अभिव्यक्ति पेड़ असमर्थित हैं कि Roslyn अभिव्यक्ति उत्पन्न करने वाले कोड को संकलित नहीं कर सकता है, लेकिन आप कुछ चीजों को काम करने के लिए काफी दूर तक पहुंचने में सक्षम होना चाहिए।

उच्च स्तर पर, यदि आप इसे अपने निर्माण कार्य में एमएसबिल्ड कार्य के रूप में कार्यान्वित करना चाहते हैं, तो आप Roslyn.Services.Workspace.LoadSolution या Roslyn.Services.Workspace.LoadStandaloneProject पर कॉल कर सकते हैं। फिर आप अपने विभिन्न तरीकों के उल्लेखों की तलाश में सिंटैक्स पेड़ों के माध्यम से चलेंगे, और फिर यह सुनिश्चित करने के लिए बाध्य करेंगे कि यह वास्तव में वह तरीका है जिसे आप सोचते हैं कि आप कॉल कर रहे हैं। वहां से, आप लैम्ब्डा सिंटैक्स नोड्स पा सकते हैं, और वहां से जो भी वाक्यविन्यास/अर्थपूर्ण विश्लेषण चाहते हैं उसे निष्पादित कर सकते हैं।

सीटीपी में कुछ नमूना परियोजनाएं हैं जो आपको उपयोगी मिल सकती हैं, जैसे कि RFxCopConsoleCS प्रोजेक्ट, जो रोज़लिन में एक साधारण FxCop-style नियम लागू करता है।

मुझे यह भी जिक्र करना चाहिए कि पार्सर रोज़लिन के लिए पूर्ण है, तो आप जितना अधिक अर्थपूर्ण जानकारी के बिना कर सकते हैं उतना ही बेहतर।