net compiler platform

62
Compiler Platform ココココココ C# コココ C# ココココ15 ココ ココココ ++C++; // 未未未未未 C

Upload: -

Post on 31-Jul-2015

2.950 views

Category:

Technology


7 download

TRANSCRIPT

Page 1: NET Compiler Platform

Compiler Platformコード解析と C# の未来

C# とともに祝 15 周年岩永信之

++C++; // 未確認飛行 C

Page 2: NET Compiler Platform

今日話すこと• 2015 世代で C# チームが提供するもの• C# 6.0• .NET Compiler Platform

• それがもたらす影響• 独自のアナイライザー作成• Code-Aware Library

• 課題• C# 7.0 以降にどうつながるか

Page 3: NET Compiler Platform

C# 6.0.NET Compiler Platform.NET チーム、 C# チームが提供する“現状”

Page 4: NET Compiler Platform

C# 6.0

• 自動プロパティの改善• expression-bodied 関数メンバー• 文字列補間• nameof 演算子• null 条件演算子• using static• 例外フィルター• catch/finally 句内での await 等々

正直なところ華はない• 機能追加よりも優先すべき

ことがあった• 限られた時間の中で

“ 2015” に間に合うもの

Page 5: NET Compiler Platform

C# 6.0

• 自動プロパティの改善• expression-bodied 関数メンバー• 文字列補間• nameof 演算子• null 条件演算子• using static• 例外フィルター• catch/finally 句内での await 等々

正直なところ華はない• 機能追加よりも優先すべき

ことがあった• 限られた時間の中で

“ 2015” に間に合うもの

C# 6.0 の話は今日はほぼしません既存資料※をご覧ください

※ http://ufcpp.net/study/csharp/ap_ver6.htmlhttp://www.slideshare.net/ufcpp/csharp6

IDE 前提の機能 .NET Compiler Platform

Page 6: NET Compiler Platform

IDE 前提の機能• nameof 演算子• 識別子名を文字列として取得できる機能

• 識別子でないものは nameof の中に書けない• 修正漏れとかをコンパイル エラーにできる

• IDE の中で真価を発揮• 修正漏れがあればリアルタイムに気付ける• リファクタリングの対象になる

• 識別子のリネームに追従する

nameof(x) == "x" コンパイルするだけならほぼただの文字列リテラル

Page 8: NET Compiler Platform

プログラミング言語の IDE 連携• C# = IDE の恩恵を強く受けれる言語• ( 今までも ) リアルタイム解析

常にバックグラウンドで解析走ってる• 「ビルド」操作した時のコンパイル時間短い• コンパイル時よりももっと早い段階で、常にどこに問題があるかわかる

• ( 今、さらに )nameof 演算子でこの傾向が強まる"x" : 文字列の中の意味をコンパイラーは知らない

実行してみて初めて問題がわかるnameof(x) : () の中の意味をコンパイラーが知ってる

リアルタイム解析の対象

Page 9: NET Compiler Platform

.NET Compiler Platform

• C# コンパイラーを 1 から作り直した• コードネーム“ Roslyn” 、製品名“ .NET Compiler Platform”

• 単なるコンパイラーではなく、プラットフォーム化旧

source executable

source executable

ブラックボックス出力しか取れない

ホワイトボックス化中間データを取れるように

• 抽象構文木の取得・書き換え• 拡張性の提供

誰でも IDE と連携できる

Page 10: NET Compiler Platform

プラットフォーム化が最優先• C# 6.0( 機能追加 ) < 作り直し ( プラットフォーム化 )• 作り直しで手いっぱいで大きいことまでできない• ( 作り直して保守が楽になったからこそ細かいことができる )

• C# 6.0• 悪く言えば、おまけ、華がない• 良く言えば、そんな大変な中よく新機能を 2015 に間に合わせた

実はおまけ

Page 11: NET Compiler Platform

Compiler Platform誰でもコンパイラーの中身に触れられる誰でも IDE 連携できるまず、「作る側」の話

Page 12: NET Compiler Platform

コード解析 (Visual Studio 標準 )

コンパイルはできるんだけど、人的ミスっぽいものを警告

アイテム テンプレートには多めに出しておいて、最後に整理して消す

人それぞれで流儀が違うけど、プロジェクト内ではそろえたい

Page 13: NET Compiler Platform

コード解析 (Visual Studio 標準 )

緑の下線 ( 警告 ):可能な限り直すべきもの

半透明 (情報 ):問題はないけども、直しようがあるもの

using System

using System

Page 14: NET Compiler Platform

コード解析 (Visual Studio 標準 )

• クイック アクション

直しようがあるものには直し方も提示、自動修正

電球マークが目印 )

一斉修正ドキュメント内全部プロジェクト内全部ソリューション内全部

(

Page 15: NET Compiler Platform

デモ

Page 16: NET Compiler Platform

プラットフォーム化• こういうコード解析を誰でも作れるように• 作れるもの :• アナライザー (analyzer)

• シンボル追加、メソッド追加、コンパイル時などのタイミングで• コードを解析して、エラー / 警告 /情報 を出す

• コード修正 (code fix)• 特定の エラー / 警告 /情報 に反応して• 修正方法を提示して• その場限り、ファイル内全部、プロジェクト内全部、ソリューション内全部

などをまとめて修正

Page 17: NET Compiler Platform

コード解析の自作に必要なもの (1)

• Visual Studio 2015 SDK• 「 Visual Studio SDK 」とかで検索、ダウンロード

https://www.visualstudio.com/en-us/downloads/visual-studio-2015-downloads-vs.aspx

Page 18: NET Compiler Platform

コード解析の自作に必要なもの (2)

• .NET Compiler Platform SDK Template• 「拡張機能と更新プログラム」で「 .NET Compiler 」とかで検索

Page 19: NET Compiler Platform

コード解析の自作• テンプレート → Extensibility → Analyzer with Code Fix

Page 20: NET Compiler Platform

デモ

コード解析本体

テスト

Visual Studio 拡張

(NuGet パッケージが作られる )

(F5 実行で Visual Studio をデバッグ起動 )

テンプレ通りの状態でアナライザーとコード修正が 1 つずつ

Page 21: NET Compiler Platform

配布• VSIX (Visual Studio Extension※)形式• Visual Studio にインストール

• 全プロジェクトから使う• Visual Studio Galleryで検索・ダウンロード可能†

• NuGet形式• プロジェクト単位で参照

• 使う・使わないをプロジェクトごとに切り替え• ライブラリと同梱で配布可能

• NuGet Galleryで検索・ダウンロード可能†

※ VSI (Visual Studio Installer) の流れを組んでるから VSIX と言うみたいファイル構造的には必要なファイルを ZIP で固めただけ

† もちろん、 VSIX/NuGet パッケージをファイル配布しても OK

Page 22: NET Compiler Platform

配布 : Visual Studio GalleryWebサイト Visual Studio上から検索

Page 23: NET Compiler Platform

配布 : NuGet GalleryWebサイト Visual Studio上から検索

Page 24: NET Compiler Platform

拡張である意味• Visual Studio/C# コンパイラー標準でない意味

• 特定分野の限定機能も提供できる⇔ 汎用• 実装方法をカスタマイズできる ⇔ 汎用性と利便性は両立しにくい• 時代遅れになったら辞めればいい ⇔ 足すより減らす方が難しい• 経験則的な機能※も提供できる ⇔ 確実な機能しか提供できない• 嫌なら使わなければいい ⇔ 嫌でも使わされる• ライブラリ固有事情を汲める ⇔ ライブラリのことは知らない

コンパイラーは…拡張なら…

※ 端的にいうと誤判定・判定漏れもあり得る

Page 25: NET Compiler Platform

拡張である意味• Visual Studio/C# コンパイラー標準でない意味

• 特定分野の限定機能も提供できる⇔ 汎用• 実装方法をカスタマイズできる ⇔ 汎用性と利便性は両立しにくい• 時代遅れになったら辞めればいい ⇔ 足すより減らす方が難しい• 経験則的な機能※も提供できる ⇔ 確実な機能しか提供できない• 嫌なら使わなければいい ⇔ 嫌でも使わされる• ライブラリ固有事情を汲める ⇔ ライブラリのことは知らない

コンパイラーは…拡張なら…

※ 端的にいうと誤判定・判定漏れもあり得る

Code-Aware ライブラリCode-Aware:( ライブラリ利用側の ) 「コードまで理解する」「コードを意識した」

• ライブラリ固有の事情にそったアナライザーを• ライブラリ本体と同梱して配布

Page 26: NET Compiler Platform

プラットフォーム化の可能性「作る側」になる人は多くない一般開発者への恩恵は何か「使う側」の話

Page 27: NET Compiler Platform

作る人 <<[壁 ]<< 使う人• 「誰でも作れる」≠「誰もが作る」• 実際とのところ、作る人は少数

• だいたい、作るの結構大変• 一度作ったらしばらく手を入れずに使い続けれる

• 誰かが作って、みんなが使う• プラットフォームには上モノが乗って初めて価値が出る

• 「 PC はアプリがなければただの箱」と同じ理屈作りやすくなる → 作られる → 使う人が便利に

ここまで揃って初めて「目玉機能」になる

Page 28: NET Compiler Platform

いくつか紹介• 「ただの箱」でないところを紹介• 現時点での話• まだまだスタート地点に立ったばかり• CTP までは変更が多くて追いにくかったし、 RC が出てまだ 1 ・ 2ヶ月• たまに、 Gallery を検索してみるといいと思う

• おおまかに分類• コード解析系• Code-Aware ライブラリ系• メタプログラミング系

Page 29: NET Compiler Platform

いくつか紹介

What 何をしてくれるものか

WhyどうしてC#コンパイラー/VSの標準機能にならないのか

どうしてライブラリ+アナライザーなのか

Owner 拡張ツールの作者( 中心人物、会社、チーム )

Page 30: NET Compiler Platform

コード解析系• 単純に静的コード解析+リファクタリング

Page 31: NET Compiler Platform

C# Essentials

WhatC# 5.0を6.0化するのをガイドしてくれる

主に ?. と =>

Why C# 5.0→6.0 の移行期にしか要らない古い形式のままで使いたい人もいる

OwnerDustin Campbell

(C#/VBチームのプログラム マネジャー)

https://github.com/DustinCampbell/CSharpEssentials

Page 32: NET Compiler Platform

C# Essentials

WhatC# 5.0を6.0化するのをガイドしてくれる

主に ?. と =>

Why C# 5.0→6.0 の移行期にしか要らない古い形式のままで使いたい人もいる

OwnerDustin Campbell

(C#/VBチームのプログラム マネジャー)

https://github.com/DustinCampbell/CSharpEssentials

Page 33: NET Compiler Platform

StyleCop Analyzer

WhatStyleCopが提供していたコード解析機能をVS拡張に

Why 趣味を選ぶ ( 人によって流儀が違う )指示が細かすぎる

OwnerSam Harwell

Coverity (静的解析ツール ベンダー)社員、MS MVP

https://github.com/DotNetAnalyzers/StyleCopAnalyzers

Page 34: NET Compiler Platform

NRefactory 6

WhatNRefactory (SharpDevelopのコード解析)がRoslyn実装に

(MonoDevelopのコード解析もNRefactory)

Why SharpDevelop チームが主導VS標準よりも細かい指示多め

Owner SharpDevelopチーム

https://visualstudiogallery.msdn.microsoft.com/68c1575b-e0bf-420d-a94b-1b0f4bcdcbcc

Page 35: NET Compiler Platform

Code Cracker

Whatコミュニティ ベースでいくつかの便利機能を実装

var強制、Regexの静的解析など

Why コミュニティを中心に開発趣味を選ぶ

Owner数名のMS MVP

https://github.com/code-cracker/code-cracker

Page 36: NET Compiler Platform

Code-Aware ライブラリ系• ライブラリ固有事情を汲み取り• そのライブラリ以外にとっては役に立たないコード解析

• 現状、あんまりいいのが見つからなかったので自作のを紹介

Page 37: NET Compiler Platform

( デモ用 ) FluentArithmetic

Whatデモ用に、最低限の機能に絞ったCode-Awareライブラリ

1.Add(2).Mul(3)みたいな書き方で整数四則演算

Why .Dive(0) を認めないとか 1.Add(2) を 3 に修正したりとか

このライブラリ以外でまったく役に立たない

Owner 自作

https://github.com/ufcpp/UfcppSample/tree/master/Chapters/DevEnv/CodeAwareLibrarySample

Page 38: NET Compiler Platform

( デモ用 ) FluentArithmetic

Whatデモ用に、最低限の機能に絞ったCode-Awareライブラリ

1.Add(2).Mul(3)みたいな書き方で整数四則演算

Why .Dive(0) を認めないとか 1.Add(2) を 3 に修正したりとか

このライブラリ以外でまったく役に立たない

Owner 自作

https://github.com/ufcpp/UfcppSample/tree/master/Chapters/DevEnv/CodeAwareLibrarySample

0割りエラー

リテラル同士の演算を短縮

Page 39: NET Compiler Platform

LazyMixin

What構造体を他の型に埋め込んで使う

has-aな実装で、is-a的な体験を提供

Why ライブラリだけで縛れない規約が多すぎるダメな書き方をエラーに、推奨の書き方をコード生成

Owner 自作

https://github.com/ufcpp/LazyMixinもっと汎用的な仕組みに書き換え中 : https://github.com/ufcpp/MixinGenerator

Page 40: NET Compiler Platform

LazyMixin

What構造体を他の型に埋め込んで使う

has-aな実装で、is-a的な体験を提供

Why ライブラリだけで縛れない規約が多すぎるダメな書き方をエラーに、推奨の書き方をコード生成

Owner 自作

https://github.com/ufcpp/LazyMixinもっと汎用的な仕組みに書き換え中 : https://github.com/ufcpp/MixinGenerator

readonly がついていると意図しない動作になるのでエラーにする

推奨の使い方をコード生成

Page 41: NET Compiler Platform

他にこんな使い方できそう• JSON ライブラリで、文字列リテラル中の JSON 解析を同梱• LINQ Provider に「使える式は何」警告機能を付ける。

Page 42: NET Compiler Platform

メタプログラミング系• コード修正というより、コード生成• アナライザー実装にすることで• コード生成元も C# ⇔ T4 テンプレート : 元がキモイ

• コード生成結果が見える ⇔ PostSharp など : 生成結果が見えない

• 現状、あんまりいいのが見つからなかったので自作等を紹介

わかりやすさ大事。特に、継続的に保守する場合

見えないとデバッグが大変。何が原因か追えない

Page 43: NET Compiler Platform

NotifyPropertyChangedGenerator

WhatINotifyPropertyChanged実装をコード生成

Why 特定用途すぎるし、実装方法にバリエーションがある

手書きがむちゃくちゃ大変

Owner neuecc (MS MVP)

https://github.com/neuecc/NotifyPropertyChangedGenerator

Page 44: NET Compiler Platform

NotifyPropertyChangedGenerator

WhatINotifyPropertyChanged実装をコード生成

Why 特定用途すぎるし、実装方法にバリエーションがある

手書きがむちゃくちゃ大変

Owner neuecc (MS MVP)

https://github.com/neuecc/NotifyPropertyChangedGenerator

Page 45: NET Compiler Platform

RecordConstructorGenerator

Whatimmutableな型のコンストラクターを書くのだるいので

コード生成するようにした

Why C# ができた当初 (15年前 ) には考慮に欠けてた

C# 7.0で状況改善しそうだけども、それまでのつなぎに

Owner 自作

https://github.com/ufcpp/RecordConstructorGenerator

Page 46: NET Compiler Platform

RecordConstructorGenerator

Whatimmutableな型のコンストラクターを書くのだるいので

コード生成するようにした

Why C# ができた当初 (15年前 ) には考慮に欠けてた

C# 7.0で状況改善しそうだけども、それまでのつなぎに

Owner 自作

https://github.com/ufcpp/RecordConstructorGenerator

Page 47: NET Compiler Platform

将来の話まだまだ始まったばかりで課題だらけこれから充実させていってほしいものもあるC# 7.0

Page 48: NET Compiler Platform

IDE 連携 ≠ Visual Studio 連携• Q. で、 Visual Studio 以外で使えるの?• A. Roslynオープンソース化の意味がここで強く効いてくる• 現状、 Visual Studio のみだけど• Xamarin Studio : 対応中みたい• OmniSharp : Roslyn化フォークがある、作業中

• ATOM, Emacs, Sublime Text, Vim, ...

• Visual Studio Code : ATOM 実装 ( コード解析は OmniSharp任せ )

Page 49: NET Compiler Platform

難易度 : 「可能にはなった」程度• コンパイラーの中身に触れるようになっただけ

var props = propertyNames.Select(p => new Property(p)).ToArray();

var docComment = GenerateDocComment(props.Select(p => p.Name));var parameterList = ParameterList().AddParameters(props.Select(p => p.ToParameter()).ToArray());var body = Block().AddStatements(props.Select(p => p.ToAssignment()).ToArray());

return ConstructorDeclaration(typeName) .WithModifiers(SyntaxTokenList.Create(PublicToken)) .WithParameterList(parameterList) .WithLeadingTrivia(docComment) .WithBody(body) .WithAdditionalAnnotations(Formatter.Annotation);

•読めた代物じゃない•書くのも大変

/// <summary>Record Constructor</summary>/// <param name="name"><see cref="Name"/></param>/// <param name="x"><see cref="X"/></param>/// <param name="y"><see cref="Y"/></param>public Point(string name = default(string), int x = default(int), int y = default(int)){ Name = name; X = x; Y = y;}

Page 50: NET Compiler Platform

解析用の新構文が欲しい•型のパターン マッチング構文が欲しい• C# 7.0 で入りそう (確度高め )

• ぶっちゃけ、“内需”だと思う

var id = statement.Left as IdentifierNameSyntax;if (id == null) continue;

if (id.Identifier.Text == p.Name) return;

特定の型の場合だけ処理アナライザーを書いているとこういうコードだらけに

if (statement.Left is IdentifierNameSyntax id && id.Identifier.Text == p.Name) return;

C# 7.0 提案パターン マッチis 演算子の拡張

Page 51: NET Compiler Platform

構文欲しい 修正用 式ツリー•組み換え可能な、 Roslyn版式ツリーが求められる• 要望としては出ているものの、構文の具体案なし

AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName(Name.Upper), IdentifierName(Name.Lower))); 「 X = x 」みたいなもの

を作るだけでこの大変さ

`${Name.Upper} = ${Name.Lower};`こういう類のメタプログラミング構文が欲しい ( この構文は適当 )

Page 52: NET Compiler Platform

参照がめんどくさい•普通のライブラリの参照方法

• アナライザーの参照

ファイルを参照

※ NuGet参照するには、 NuGet の startup スクリプトに参照設定を書かなきゃ行けない( アナライザー作成テンプレートには最初からそういう設定スクリプトが書かれてる )

ファイルを参照

Page 53: NET Compiler Platform

メタプログラミングはまだ妥協的• メタプログラミングでは• 生成元・生成結果両方見えてほしい• ただ、元と結果は明確に分離したい

public class Sample1{ public string Name { get; set; } public int X { get; set; } public int Y { get; set; }}

public class Sample1 : INotifyPropertyChanged{ public int X { get { return x; } set { SetProperty(ref x, value, xPropertyChangedEventArgs); } } public int Y { get { return y; } set { SetProperty(ref y, value, yPropertyChangedEventArgs); } }

#region NotifyPropertyChangedGenerator

public event PropertyChangedEventHandler PropertyChanged;

private int x; private static readonly PropertyChangedEventArgs xPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(X)); private int y; private static readonly PropertyChangedEventArgs yPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(Y));

元情報

生成結果• デバッグのためだけに見たい• コードを書く上ではノイズ

Page 54: NET Compiler Platform

コンパイラーの負の遺産• Visual Studio/C# コンパイラー標準でない意味 (再掲、抜粋 )

• 時代遅れになったら辞めればいい ⇔ 足すより減らす方が難しい• 経験則的な機能※も提供できる ⇔ 確実な機能しか提供できない

• C# 7.0 提案• null非許容参照型• メソッド コントラクト

コンパイラーは…拡張なら…

この辺りの制限がきつくて足せない機能がある

• 互換性を崩さないのが無理• 100%確実な判定が無理

Page 55: NET Compiler Platform

null非許容参照型• 今の C# に欠けているもの

値型 参照型

許容 T? T

非許容 Tこれがない

static void F(string s){ if (s == null) throw new ArgumentNullException(nameof(s));}

現状の書き方メソッドのシグネチャだけ見てnull許容かどうか判定できない

煩雑

Page 56: NET Compiler Platform

null非許容参照型• 今の C# に欠けているもの

値型 参照型

許容 T? T?

非許容 T T!

null非許容参照型

static void F(string! s){}

C# 7.0 提案の書き方 • 誰が見ても null非許容• null チェックはコンパイラー生

Page 57: NET Compiler Platform

null非許容参照型と互換性問題• F(string s) を F(string! s) に変えると、利用側を壊す• 今まで null チェックをサボっていた人がいたら• 例外 catch で済ませている人がいたら

• 標準ライブラリにちゃんと ! が付いてないと利便性半減互換性と利便性にトレードオフ

Page 58: NET Compiler Platform

null非許容参照型と確実性•一時的に null になっていないといけない場面がある• 配列とか

• 特に、コレクションの実装とかで• List<T>• HashSet<T>

• この性質と、マルチスレッド動作が合わさると判定不能

var array = new string[N];for (var i = 0; i < N; i++){ array[i] = "";}

この間は絶対に null

最初に大きめの配列をとっておいて、そのうち Nマスだけ使う

Page 59: NET Compiler Platform

null非許容参照型のアナライザー実装• アナライザーでなら実装簡単• 互換性が必要な場面では使わなければいい• 誤判定のリスク < ないことによる不便

•事実、実装がある• 今でも、 ReSharper とかの静的解析ツールはやってる• C# チームも、一度アナライザーで実装してみてる

• 実装 : https://github.com/mattwar/nullaby• その報告 : https://github.com/dotnet/roslyn/issues/2119

• もしかしたら、 C# 7.0 はこのまま、一部アナライザー実装になるかも

Page 60: NET Compiler Platform

言語機能のアナライザー実装の課題•一部分だけアナライザー?• string! ← こういう書き方を解釈するのはコンパイラー機能• string! の非 null を解析するのはアナライザー

• 機能が on/off できるコンパイラー機能?• 同じバージョンの C# を使っているはずでも、コンパイルできる環境

とできない環境ができる

挙動的にはかなりキモい

Page 61: NET Compiler Platform

まとめ

Page 62: NET Compiler Platform

まとめ• C# 6.0 、 Compiler Platform• IDE との連携性が実は主役、 C# 6.0 はちょい役

• Platform 化の恩恵• 皆が作れる• Code-Aware である

• ライブラリ固有の事情をくめる• 経験則

• 特定文脈によった解析とか、誤判定 (判定漏れ ) が許容されうる

• C#公式機能ですらアナライザーベースになるかも• null非許容参照型 ( あとたぶん、メソッド コントラクトも )