SlideShare une entreprise Scribd logo
1  sur  38
Extending C# with Roslyn
and Code-Aware Libraries
Carlo Pescio
Goals
Perspective on a new technology
Stimulate new ideas
Some practice
How to start using it
A simple (but not trivial) example
Back on a larger perspective
Overcoming an OOP issue / C# limitation
Extend vs. Constrain
Libraries de facto extend the language
“Libraries cannot provide new inabilities”
Crista Lopes / Mark Miller
(http://tagide.com/blog/research/constraints)
Adding “inabilities” to the language through a library??
WHY ???
Example 1
public static class To<I2> where I2 : class
{
public static I2 Safely<I1>(I1 from)
where I1 : class
{
return from as I2;
}
}
where I2 : interface
where I1 : interface
Ifc2 q1 = To<Ifc2>.Safely(o1);
Example 2
domain layer
service layer
application layer
database
persistence layer
Who’s gonna check?
Not the compiler
Code review?
Some external tool?
The library / module / code itself!
Code-Aware Library
Leveraging the Roslyn compiler model
A library can include “analyzers”
Analyzers run at compile time
will see code using the library
Library + Analyzer = Code Aware Library
Can provide warnings or errors (and fixes)
Cannot extend syntax (no new keywords)
First steps
Install the .NET Compiler Platform ("Roslyn") SDK
from aka.ms/roslynsdktemplates
Create a new project:
File | New Project | C# | Extensibility ->
Analyzer with Code Fix (NuGet + VSIX)
-> sample project will warn if a type name
contains lowercase letters
Inside the Solution
- Analyzer project
- “Debugging project” (VSIX)
- (Test project)
Running the debugging project will open another
instance of visual studio with your analyzer active.
Create / Open a solution there using your library
Use that to develop and debug your analyzer
Implementation inheritance to provide
common behavior & polymorphic hooks.
Attribute to be found by roslyn
Analizers
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class InterfaceCheckAnalyzer : DiagnosticAnalyzer
Not a full tutorial – see “resources” for details
Minimal set-up (1)
private static readonly LocalizableString Rule1Title = "'To' parameter must be an interface";
private static readonly LocalizableString Rule1MessageFormat =
"Safe cast error: 'To' type parameter must be an interface but is {0}";
private static readonly LocalizableString Rule1Description =
"Safe casts must be from <Interface1> to <Interface2>";
private static readonly string Rule1Category = "Typing";
private static DiagnosticDescriptor Rule1 = new
DiagnosticDescriptor(DiagnosticId, Rule1Title, Rule1MessageFormat,
Rule1Category, DiagnosticSeverity.Error,
isEnabledByDefault: true, description: Rule1Description);
Diagnostics Descriptors are templates for warning /
error messages
public override ImmutableArray<DiagnosticDescriptor>
SupportedDiagnostics { get { … } }
Minimal set-up (2)
Initialize: register callbacks on syntax tree
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(
AnalyzeSafelyCall, SyntaxKind.InvocationExpression);
}
Several SyntaxKind, like type declaration etc.
Key notion: Syntax tree
A syntactic tree of the (parsed) source code.
Rich structure, “learn by exploration”
Key notion: Semantic model
Answers questions like “in which class “Safely”
was declared?”
var safelySymbol =
context.SemanticModel.GetSymbolInfo(memberAccessExpr).Symbol as
IMethodSymbol
Example: Interface checking
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeSafelyCall,
SyntaxKind.InvocationExpression);
}
public void AnalyzeSafelyCall(SyntaxNodeAnalysisContext context)
{
var invocationExpr = (InvocationExpressionSyntax)context.Node;
var memberAccessExpr = invocationExpr.Expression as
MemberAccessExpressionSyntax;
if(memberAccessExpr?.Name.ToString() != "Safely") return;
var safelySymbol = context.SemanticModel.
GetSymbolInfo(memberAccessExpr).Symbol
as IMethodSymbol;
var safelyContainingType = safelySymbol?.ContainingType;
var safelyTypeName = safelyContainingType?.MetadataName;
if(safelyTypeName != "To`1") return;
Emitting diagnostics
var safelyContainingType = safelySymbol?.ContainingType;
var safelyTypeName = safelyContainingType?.MetadataName;
if(safelyTypeName != "To`1") return;
var toTypeArg = safelyContainingType.TypeArguments[0];
if(toTypeArg.TypeKind != TypeKind.Interface)
{
var toExpression = (GenericNameSyntax)memberAccessExpr.Expression;
var toTypeArgs = toExpression.TypeArgumentList;
var diagnostic =
Diagnostic.Create(Rule1, toTypeArgs.GetLocation(), toTypeArg.Name);
context.ReportDiagnostic(diagnostic);
}
Compile-Time errors
interface I1
{ }
interface I2
{ }
class C1 : I1
{ }
class Program
{
static void Main(string[] args)
{
I1 o1 = new C1();
I2 q1 = To<I2>.Safely(o1);
object v = To<string>.Safely("aa");
}
}
Extending C#?
Libraries cannot introduce “inabilities” and constraints
Code-aware libraries can!
=> Inability to make an unsafe cross-cast
Inception
On Growth and Software
nCrafts 2016
Paris
http://videos.
ncrafts.io/
video/
167699028
Vector drawing for dummies
Growth Model
- New shape types will come over time
- New behaviors will come over time
- Behaviors can be composed out of a fixed core
- That entire menu only requires {center, bounding box, move}
I’m dealing only with geometry right now
public interface ITool
{
IEnumerable<IShape> ApplyTo(List<IShape> source);
}
public interface IShape
{
Point Center();
Box BoundingBox();
IShape Move(Point newCenter);
}
Extensible? Sure!
- New shape type => new class
- New tool => new class
- Given the right core methods in IShape
- Geometric core methods will saturate anyway
Breaking the growth model
We only have geometry so far
I want rendering (new core methods!?)
I may want only geometry
I may want only rendering
I may want both
=> Geometry and Rendering in distinct artifacts?
partial class Circle
{
private readonly Point c;
private readonly double r;
public Circle( Point center, double radius )
{
c = center;
r = radius;
}
}
interface IShape
{
IShape Move(Point newCenter);
}
partial class Circle : IShape
{
public IShape Move(Point newCenter)
{
return new Circle(newCenter, radius);
}
}
interface IDrawing
{
void Render();
}
partial class Circle : IDrawing
{
public void Render()
{
Console.WriteLine(
"I'm a Circle with radius " +
radius.ToString());
}
}
static void Main(string[] args)
{
IShape c = new Circle(new Point(10, 10), 5);
IDrawing d = c as IDrawing;
d.Render();
}
constraint
IShape => IDrawing
1) Reframe as Library
IDrawing d = c safely_as IDrawing;
IDrawing d = To<IDrawing>.Safely(c);
constraint IShape => IDrawing
[module: Constraint.Implies<IShape,IDrawing>]
[module:
Constraint.Implies(typeof(SafelyAsSample.IShape),
typeof(SafelyAsSample.IDrawing))]
2) Define the rules
To<type>.Safely(expression);
- type must be an interface (I2)
- expression must be typed as an interface (I1)
- there must be a constraint I1 => I2
[module:Constraint.Implies(typeof(type1),typeof(type2))]
- type1 and type2 must be interfaces
- Every class implementing type1 must
implement type2
3) Turn rules into analyzer
Local checks (easy)
- constraint parameters must be interfaces
- To and Safely parameters must be interfaces
Global checks (“harder”)
- “every type implementing type1 must implement type2”
- “there must be a constraint I1 => I2” (somewhere)
Analyzer callbacks
public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(
compilationContext =>
{
Solution wholeThing = new Solution();
compilationContext.RegisterSyntaxNodeAction(
wholeThing.AnalyzeImpliesDeclaration,
SyntaxKind.Attribute);
compilationContext.RegisterSyntaxNodeAction(
wholeThing.AnalyzeSafelyCall,
SyntaxKind.InvocationExpression);
compilationContext.RegisterSymbolAction(
wholeThing.RegisterNamedTypes,
SymbolKind.NamedType);
compilationContext.RegisterCompilationEndAction(
wholeThing.AnalyzeConstraintsOnTypes);
});
}
Prototype structure
Responsibilities
- Contact surface with Roslyn: attribute + inheritance + overrides
- Creates a Solution object for each compilation phase
- Sets up the callbacks to invoke members into the Solutions object
- That provides an isolated, consistent state for each compilation phase
Responsibilities
- Scans types, attributes, function calls
- Does all the simple local checking
- Stores casts, types and constraints in the CheckedTypeSystems
- Merges and reports local and global diagnostics
- Currently a bit fat
Responsibilities
- Keeps track of all types, constraints and safe casts
- Does all the global type checking
- Returns diagnostics object (does not emit)
- Only contact with Roslyn is through ITypeSymbol
… and it works 
[module: Constraint.Implies(typeof(SafelyAsSample.IShape),
typeof(SafelyAsSample.IGeometry))]
[module: Constraint.Implies(typeof(SafelyAsSample.IShape),
typeof(SafelyAsSample.IDrawing))]
[module: Constraint.Implies(typeof(SafelyAsSample.Circle),
typeof(SafelyAsSample.Circle))]
public partial class Square : IGeometry { }
static void Main(string[] args)
{
IShape s1 = new Circle(3);
IDrawing d1 = To<IDrawing>.Safely(s1);
Circle d2 = To<Circle>.Safely(s1);
IDrawing d3 = To<IDrawing>.Safely(d2);
INotImplemented n = To<INotImplemented>.Safely(s1);
}
Conclusions
- Code aware libraries open new possibilities
- Create better, safer libraries
- Do some “language hacking”
without changing the compiler
- And therefore making your language
extensions useful for everyone
Resources
Dustin Campbell talk at Microsoft Build:
Analyzers and the Rise of Code-Aware Libraries
https://www.youtube.com/watch?v=Ip6wrpYFHhE
Alex Turner, Use Roslyn to Write a Live Code Analyzer for Your API
https://msdn.microsoft.com/en-us/magazine/dn879356
Code on github
https://github.com/carlopescio/safe_cast
Get in touch
carlo.pescio@gmail.com
@CarloPescio
http://eptacom.net

Contenu connexe

Tendances

Pyconie 2012
Pyconie 2012Pyconie 2012
Pyconie 2012
Yaqi Zhao
 

Tendances (19)

Introduzione al TDD
Introduzione al TDDIntroduzione al TDD
Introduzione al TDD
 
How to Profit from Static Analysis
How to Profit from Static AnalysisHow to Profit from Static Analysis
How to Profit from Static Analysis
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for Programmers
 
Testing React Applications
Testing React ApplicationsTesting React Applications
Testing React Applications
 
Streams or Loops? Java 8 Stream API by Niki Petkov - Proxiad Bulgaria
Streams or Loops? Java 8 Stream API by Niki Petkov - Proxiad BulgariaStreams or Loops? Java 8 Stream API by Niki Petkov - Proxiad Bulgaria
Streams or Loops? Java 8 Stream API by Niki Petkov - Proxiad Bulgaria
 
Pyconie 2012
Pyconie 2012Pyconie 2012
Pyconie 2012
 
Google C++ Testing Framework in Visual Studio 2008
Google C++ Testing Framework in Visual Studio 2008Google C++ Testing Framework in Visual Studio 2008
Google C++ Testing Framework in Visual Studio 2008
 
Inside PyMongo - MongoNYC
Inside PyMongo - MongoNYCInside PyMongo - MongoNYC
Inside PyMongo - MongoNYC
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Software engineering ⊇ Software testing
Software engineering ⊇ Software testingSoftware engineering ⊇ Software testing
Software engineering ⊇ Software testing
 
SymfonyCon Berlin 2016 Jenkins Deployment Pipelines
SymfonyCon Berlin 2016 Jenkins Deployment PipelinesSymfonyCon Berlin 2016 Jenkins Deployment Pipelines
SymfonyCon Berlin 2016 Jenkins Deployment Pipelines
 
NodeJS Spring style Inversifyjs
NodeJS Spring style InversifyjsNodeJS Spring style Inversifyjs
NodeJS Spring style Inversifyjs
 
Decompiling Java - SCAM2009 Presentation
Decompiling Java - SCAM2009 PresentationDecompiling Java - SCAM2009 Presentation
Decompiling Java - SCAM2009 Presentation
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
 
Navigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupNavigating the xDD Alphabet Soup
Navigating the xDD Alphabet Soup
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
 
JavaScript Editions ES7, ES8 and ES9 vs V8
JavaScript Editions ES7, ES8 and ES9 vs V8JavaScript Editions ES7, ES8 and ES9 vs V8
JavaScript Editions ES7, ES8 and ES9 vs V8
 
TDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesTDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing Techniques
 
Unit tests in node.js
Unit tests in node.jsUnit tests in node.js
Unit tests in node.js
 

Similaire à Extending C# with Roslyn and Code Aware Libraries

Dr archana dhawan bajaj - csharp fundamentals slides
Dr archana dhawan bajaj - csharp fundamentals slidesDr archana dhawan bajaj - csharp fundamentals slides
Dr archana dhawan bajaj - csharp fundamentals slides
Dr-archana-dhawan-bajaj
 

Similaire à Extending C# with Roslyn and Code Aware Libraries (20)

Twins: Object Oriented Programming and Functional Programming
Twins: Object Oriented Programming and Functional ProgrammingTwins: Object Oriented Programming and Functional Programming
Twins: Object Oriented Programming and Functional Programming
 
Robots in Swift
Robots in SwiftRobots in Swift
Robots in Swift
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Design for Testability
Design for TestabilityDesign for Testability
Design for Testability
 
Thinking In Swift
Thinking In SwiftThinking In Swift
Thinking In Swift
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - Warburton
 
PVS-Studio and static code analysis technique
PVS-Studio and static code analysis techniquePVS-Studio and static code analysis technique
PVS-Studio and static code analysis technique
 
C# for beginners
C# for beginnersC# for beginners
C# for beginners
 
Dr archana dhawan bajaj - csharp fundamentals slides
Dr archana dhawan bajaj - csharp fundamentals slidesDr archana dhawan bajaj - csharp fundamentals slides
Dr archana dhawan bajaj - csharp fundamentals slides
 
Clean Code for East Bay .NET User Group
Clean Code for East Bay .NET User GroupClean Code for East Bay .NET User Group
Clean Code for East Bay .NET User Group
 
Clean Code at Silicon Valley Code Camp 2011 (02/17/2012)
Clean Code at Silicon Valley Code Camp 2011 (02/17/2012)Clean Code at Silicon Valley Code Camp 2011 (02/17/2012)
Clean Code at Silicon Valley Code Camp 2011 (02/17/2012)
 
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
 
OOP, API Design and MVP
OOP, API Design and MVPOOP, API Design and MVP
OOP, API Design and MVP
 
React native
React nativeReact native
React native
 
Static code analysis: what? how? why?
Static code analysis: what? how? why?Static code analysis: what? how? why?
Static code analysis: what? how? why?
 
C#ppt
C#pptC#ppt
C#ppt
 
Objective c
Objective cObjective c
Objective c
 
Most Useful Design Patterns
Most Useful Design PatternsMost Useful Design Patterns
Most Useful Design Patterns
 
L04 Software Design 2
L04 Software Design 2L04 Software Design 2
L04 Software Design 2
 
Java - A broad introduction
Java - A broad introductionJava - A broad introduction
Java - A broad introduction
 

Dernier

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
anilsa9823
 

Dernier (20)

HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 

Extending C# with Roslyn and Code Aware Libraries

  • 1.
  • 2. Extending C# with Roslyn and Code-Aware Libraries Carlo Pescio
  • 3. Goals Perspective on a new technology Stimulate new ideas Some practice How to start using it A simple (but not trivial) example Back on a larger perspective Overcoming an OOP issue / C# limitation
  • 4. Extend vs. Constrain Libraries de facto extend the language “Libraries cannot provide new inabilities” Crista Lopes / Mark Miller (http://tagide.com/blog/research/constraints) Adding “inabilities” to the language through a library?? WHY ???
  • 5. Example 1 public static class To<I2> where I2 : class { public static I2 Safely<I1>(I1 from) where I1 : class { return from as I2; } } where I2 : interface where I1 : interface Ifc2 q1 = To<Ifc2>.Safely(o1);
  • 6. Example 2 domain layer service layer application layer database persistence layer
  • 7. Who’s gonna check? Not the compiler Code review? Some external tool? The library / module / code itself!
  • 8. Code-Aware Library Leveraging the Roslyn compiler model A library can include “analyzers” Analyzers run at compile time will see code using the library Library + Analyzer = Code Aware Library Can provide warnings or errors (and fixes) Cannot extend syntax (no new keywords)
  • 9. First steps Install the .NET Compiler Platform ("Roslyn") SDK from aka.ms/roslynsdktemplates Create a new project: File | New Project | C# | Extensibility -> Analyzer with Code Fix (NuGet + VSIX) -> sample project will warn if a type name contains lowercase letters
  • 10. Inside the Solution - Analyzer project - “Debugging project” (VSIX) - (Test project) Running the debugging project will open another instance of visual studio with your analyzer active. Create / Open a solution there using your library Use that to develop and debug your analyzer
  • 11. Implementation inheritance to provide common behavior & polymorphic hooks. Attribute to be found by roslyn Analizers [DiagnosticAnalyzer(LanguageNames.CSharp)] public class InterfaceCheckAnalyzer : DiagnosticAnalyzer Not a full tutorial – see “resources” for details
  • 12. Minimal set-up (1) private static readonly LocalizableString Rule1Title = "'To' parameter must be an interface"; private static readonly LocalizableString Rule1MessageFormat = "Safe cast error: 'To' type parameter must be an interface but is {0}"; private static readonly LocalizableString Rule1Description = "Safe casts must be from <Interface1> to <Interface2>"; private static readonly string Rule1Category = "Typing"; private static DiagnosticDescriptor Rule1 = new DiagnosticDescriptor(DiagnosticId, Rule1Title, Rule1MessageFormat, Rule1Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Rule1Description); Diagnostics Descriptors are templates for warning / error messages public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { … } }
  • 13. Minimal set-up (2) Initialize: register callbacks on syntax tree public override void Initialize(AnalysisContext context) { context.RegisterSyntaxNodeAction( AnalyzeSafelyCall, SyntaxKind.InvocationExpression); } Several SyntaxKind, like type declaration etc.
  • 14. Key notion: Syntax tree A syntactic tree of the (parsed) source code. Rich structure, “learn by exploration”
  • 15. Key notion: Semantic model Answers questions like “in which class “Safely” was declared?” var safelySymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol
  • 16. Example: Interface checking public override void Initialize(AnalysisContext context) { context.RegisterSyntaxNodeAction(AnalyzeSafelyCall, SyntaxKind.InvocationExpression); } public void AnalyzeSafelyCall(SyntaxNodeAnalysisContext context) { var invocationExpr = (InvocationExpressionSyntax)context.Node; var memberAccessExpr = invocationExpr.Expression as MemberAccessExpressionSyntax; if(memberAccessExpr?.Name.ToString() != "Safely") return; var safelySymbol = context.SemanticModel. GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol; var safelyContainingType = safelySymbol?.ContainingType; var safelyTypeName = safelyContainingType?.MetadataName; if(safelyTypeName != "To`1") return;
  • 17. Emitting diagnostics var safelyContainingType = safelySymbol?.ContainingType; var safelyTypeName = safelyContainingType?.MetadataName; if(safelyTypeName != "To`1") return; var toTypeArg = safelyContainingType.TypeArguments[0]; if(toTypeArg.TypeKind != TypeKind.Interface) { var toExpression = (GenericNameSyntax)memberAccessExpr.Expression; var toTypeArgs = toExpression.TypeArgumentList; var diagnostic = Diagnostic.Create(Rule1, toTypeArgs.GetLocation(), toTypeArg.Name); context.ReportDiagnostic(diagnostic); }
  • 18. Compile-Time errors interface I1 { } interface I2 { } class C1 : I1 { } class Program { static void Main(string[] args) { I1 o1 = new C1(); I2 q1 = To<I2>.Safely(o1); object v = To<string>.Safely("aa"); } }
  • 19. Extending C#? Libraries cannot introduce “inabilities” and constraints Code-aware libraries can! => Inability to make an unsafe cross-cast
  • 20. Inception On Growth and Software nCrafts 2016 Paris http://videos. ncrafts.io/ video/ 167699028
  • 22. Growth Model - New shape types will come over time - New behaviors will come over time - Behaviors can be composed out of a fixed core - That entire menu only requires {center, bounding box, move} I’m dealing only with geometry right now
  • 23. public interface ITool { IEnumerable<IShape> ApplyTo(List<IShape> source); } public interface IShape { Point Center(); Box BoundingBox(); IShape Move(Point newCenter); }
  • 24. Extensible? Sure! - New shape type => new class - New tool => new class - Given the right core methods in IShape - Geometric core methods will saturate anyway
  • 25. Breaking the growth model We only have geometry so far I want rendering (new core methods!?) I may want only geometry I may want only rendering I may want both => Geometry and Rendering in distinct artifacts?
  • 26. partial class Circle { private readonly Point c; private readonly double r; public Circle( Point center, double radius ) { c = center; r = radius; } } interface IShape { IShape Move(Point newCenter); } partial class Circle : IShape { public IShape Move(Point newCenter) { return new Circle(newCenter, radius); } } interface IDrawing { void Render(); } partial class Circle : IDrawing { public void Render() { Console.WriteLine( "I'm a Circle with radius " + radius.ToString()); } } static void Main(string[] args) { IShape c = new Circle(new Point(10, 10), 5); IDrawing d = c as IDrawing; d.Render(); } constraint IShape => IDrawing
  • 27. 1) Reframe as Library IDrawing d = c safely_as IDrawing; IDrawing d = To<IDrawing>.Safely(c); constraint IShape => IDrawing [module: Constraint.Implies<IShape,IDrawing>] [module: Constraint.Implies(typeof(SafelyAsSample.IShape), typeof(SafelyAsSample.IDrawing))]
  • 28. 2) Define the rules To<type>.Safely(expression); - type must be an interface (I2) - expression must be typed as an interface (I1) - there must be a constraint I1 => I2 [module:Constraint.Implies(typeof(type1),typeof(type2))] - type1 and type2 must be interfaces - Every class implementing type1 must implement type2
  • 29. 3) Turn rules into analyzer Local checks (easy) - constraint parameters must be interfaces - To and Safely parameters must be interfaces Global checks (“harder”) - “every type implementing type1 must implement type2” - “there must be a constraint I1 => I2” (somewhere)
  • 30. Analyzer callbacks public override void Initialize(AnalysisContext context) { context.RegisterCompilationStartAction( compilationContext => { Solution wholeThing = new Solution(); compilationContext.RegisterSyntaxNodeAction( wholeThing.AnalyzeImpliesDeclaration, SyntaxKind.Attribute); compilationContext.RegisterSyntaxNodeAction( wholeThing.AnalyzeSafelyCall, SyntaxKind.InvocationExpression); compilationContext.RegisterSymbolAction( wholeThing.RegisterNamedTypes, SymbolKind.NamedType); compilationContext.RegisterCompilationEndAction( wholeThing.AnalyzeConstraintsOnTypes); }); }
  • 32. Responsibilities - Contact surface with Roslyn: attribute + inheritance + overrides - Creates a Solution object for each compilation phase - Sets up the callbacks to invoke members into the Solutions object - That provides an isolated, consistent state for each compilation phase
  • 33. Responsibilities - Scans types, attributes, function calls - Does all the simple local checking - Stores casts, types and constraints in the CheckedTypeSystems - Merges and reports local and global diagnostics - Currently a bit fat
  • 34. Responsibilities - Keeps track of all types, constraints and safe casts - Does all the global type checking - Returns diagnostics object (does not emit) - Only contact with Roslyn is through ITypeSymbol
  • 35. … and it works  [module: Constraint.Implies(typeof(SafelyAsSample.IShape), typeof(SafelyAsSample.IGeometry))] [module: Constraint.Implies(typeof(SafelyAsSample.IShape), typeof(SafelyAsSample.IDrawing))] [module: Constraint.Implies(typeof(SafelyAsSample.Circle), typeof(SafelyAsSample.Circle))] public partial class Square : IGeometry { } static void Main(string[] args) { IShape s1 = new Circle(3); IDrawing d1 = To<IDrawing>.Safely(s1); Circle d2 = To<Circle>.Safely(s1); IDrawing d3 = To<IDrawing>.Safely(d2); INotImplemented n = To<INotImplemented>.Safely(s1); }
  • 36. Conclusions - Code aware libraries open new possibilities - Create better, safer libraries - Do some “language hacking” without changing the compiler - And therefore making your language extensions useful for everyone
  • 37. Resources Dustin Campbell talk at Microsoft Build: Analyzers and the Rise of Code-Aware Libraries https://www.youtube.com/watch?v=Ip6wrpYFHhE Alex Turner, Use Roslyn to Write a Live Code Analyzer for Your API https://msdn.microsoft.com/en-us/magazine/dn879356 Code on github https://github.com/carlopescio/safe_cast