From d345513b3bd8b34dd7cb43f87d702edbc1ab2ad4 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 28 Feb 2006 19:46:22 +0000 Subject: [PATCH] Add Code Analysis AddIn (can control which FxCop rules to run). For some reason, FxCop is not being run inside SharpDevelop; but it is when running msbuild.exe from the command line. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@1192 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Misc/CodeAnalysis/CodeAnalysis.addin | 20 + .../Misc/CodeAnalysis/CodeAnalysis.csproj | 77 ++++ src/AddIns/Misc/CodeAnalysis/CodeAnalysis.sln | 6 + .../Configuration/AssemblyInfo.cs | 21 ++ .../Src/AnalysisProjectOptions.Designer.cs | 153 ++++++++ .../Src/AnalysisProjectOptions.cs | 343 ++++++++++++++++++ .../Src/AnalysisProjectOptions.resx | 126 +++++++ .../Src/AnalysisProjectOptionsPanel.cs | 40 ++ src/AddIns/Misc/CodeAnalysis/Src/FxCopRule.cs | 112 ++++++ .../Misc/CodeAnalysis/Src/FxCopWrapper.cs | 203 +++++++++++ src/Main/Core/Project/ICSharpCode.Core.csproj | 1 + .../Project/Src/Util/AppDomainLaunchHelper.cs | 51 +++ src/SharpDevelop.sln | 9 +- 13 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin create mode 100644 src/AddIns/Misc/CodeAnalysis/CodeAnalysis.csproj create mode 100644 src/AddIns/Misc/CodeAnalysis/CodeAnalysis.sln create mode 100644 src/AddIns/Misc/CodeAnalysis/Configuration/AssemblyInfo.cs create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.Designer.cs create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.resx create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptionsPanel.cs create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/FxCopRule.cs create mode 100644 src/AddIns/Misc/CodeAnalysis/Src/FxCopWrapper.cs create mode 100644 src/Main/Core/Project/Src/Util/AppDomainLaunchHelper.cs diff --git a/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin new file mode 100644 index 0000000000..8ad4057baa --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.addin @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.csproj b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.csproj new file mode 100644 index 0000000000..d264f6dda3 --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.csproj @@ -0,0 +1,77 @@ + + + Library + ICSharpCode.CodeAnalysis + CodeAnalysis + Debug + AnyCPU + {3EAA45A9-735C-4AC7-A799-947B93EA449D} + ..\..\..\..\AddIns\AddIns\Misc\CodeAnalysis\ + False + False + False + Auto + 4194304 + AnyCPU + 4096 + 4 + false + + + obj\ + obj\Debug\ + False + DEBUG;TRACE + true + Full + True + + + obj\ + obj\Release\ + True + TRACE + False + None + False + + + + + + + + + + + Always + + + + Configuration\GlobalAssemblyInfo.cs + + + + AnalysisProjectOptions.cs + + + + AnalysisProjectOptions.cs + + + + + + + {2748AD25-9C63-4E12-877B-4DCE96FBED54} + ICSharpCode.SharpDevelop + False + + + {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} + ICSharpCode.Core + False + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.sln b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.sln new file mode 100644 index 0000000000..9ace730fd7 --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/CodeAnalysis.sln @@ -0,0 +1,6 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# SharpDevelop 2.1.0.1190 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis", "CodeAnalysis.csproj", "{3EAA45A9-735C-4AC7-A799-947B93EA449D}" +EndProject +Global +EndGlobal diff --git a/src/AddIns/Misc/CodeAnalysis/Configuration/AssemblyInfo.cs b/src/AddIns/Misc/CodeAnalysis/Configuration/AssemblyInfo.cs new file mode 100644 index 0000000000..d29eaefb2f --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Configuration/AssemblyInfo.cs @@ -0,0 +1,21 @@ +// +// +// +// +// $Revision$ +// + +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("CodeAnalysis")] +[assembly: AssemblyDescription("AddIn for SharpDevelop 2.0")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] diff --git a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.Designer.cs b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.Designer.cs new file mode 100644 index 0000000000..364dfbca4e --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.Designer.cs @@ -0,0 +1,153 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: ${DATE} + * Time: ${TIME} + */ +namespace ICSharpCode.CodeAnalysis +{ + partial class AnalysisProjectOptions : System.Windows.Forms.UserControl + { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Disposes resources used by the control. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + System.Windows.Forms.SplitContainer splitContainer1; + System.Windows.Forms.Panel panel1; + this.ruleLabel = new System.Windows.Forms.Label(); + this.warningOrErrorLabel = new System.Windows.Forms.Label(); + this.enableCheckBox = new System.Windows.Forms.CheckBox(); + this.ruleTreeView = new System.Windows.Forms.TreeView(); + splitContainer1 = new System.Windows.Forms.SplitContainer(); + panel1 = new System.Windows.Forms.Panel(); + splitContainer1.Panel1.SuspendLayout(); + splitContainer1.Panel2.SuspendLayout(); + splitContainer1.SuspendLayout(); + panel1.SuspendLayout(); + this.SuspendLayout(); + // + // splitContainer1 + // + splitContainer1.BackColor = System.Drawing.SystemColors.ControlDark; + splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; + splitContainer1.Location = new System.Drawing.Point(1, 1); + splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + splitContainer1.Panel1.Controls.Add(this.ruleLabel); + splitContainer1.Panel1.RightToLeft = System.Windows.Forms.RightToLeft.No; + // + // splitContainer1.Panel2 + // + splitContainer1.Panel2.Controls.Add(this.warningOrErrorLabel); + splitContainer1.Panel2.RightToLeft = System.Windows.Forms.RightToLeft.No; + splitContainer1.RightToLeft = System.Windows.Forms.RightToLeft.No; + splitContainer1.Size = new System.Drawing.Size(375, 17); + splitContainer1.SplitterDistance = 208; + splitContainer1.SplitterWidth = 2; + splitContainer1.TabIndex = 1; + // + // ruleLabel + // + this.ruleLabel.BackColor = System.Drawing.SystemColors.ControlLight; + this.ruleLabel.Dock = System.Windows.Forms.DockStyle.Fill; + this.ruleLabel.Location = new System.Drawing.Point(0, 0); + this.ruleLabel.Name = "ruleLabel"; + this.ruleLabel.Size = new System.Drawing.Size(208, 17); + this.ruleLabel.TabIndex = 0; + this.ruleLabel.Text = "${res:ICSharpCode.CodeAnalysis.Rule}"; + // + // warningOrErrorLabel + // + this.warningOrErrorLabel.BackColor = System.Drawing.SystemColors.ControlLight; + this.warningOrErrorLabel.Dock = System.Windows.Forms.DockStyle.Fill; + this.warningOrErrorLabel.Location = new System.Drawing.Point(0, 0); + this.warningOrErrorLabel.Name = "warningOrErrorLabel"; + this.warningOrErrorLabel.Size = new System.Drawing.Size(165, 17); + this.warningOrErrorLabel.TabIndex = 0; + this.warningOrErrorLabel.Text = "${res:ICSharpCode.CodeAnalysis.ProjectOptions.WarningOrError}"; + // + // panel1 + // + panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + panel1.BackColor = System.Drawing.SystemColors.InactiveCaption; + panel1.Controls.Add(splitContainer1); + panel1.Location = new System.Drawing.Point(15, 33); + panel1.Name = "panel1"; + panel1.Padding = new System.Windows.Forms.Padding(1); + panel1.Size = new System.Drawing.Size(377, 19); + panel1.TabIndex = 3; + // + // enableCheckBox + // + this.enableCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.enableCheckBox.Location = new System.Drawing.Point(15, 4); + this.enableCheckBox.Name = "enableCheckBox"; + this.enableCheckBox.Size = new System.Drawing.Size(376, 24); + this.enableCheckBox.TabIndex = 0; + this.enableCheckBox.Text = "${res:ICSharpCode.CodeAnalysis.ProjectOptions.Enable}"; + this.enableCheckBox.UseVisualStyleBackColor = true; + // + // ruleTreeView + // + this.ruleTreeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ruleTreeView.CheckBoxes = true; + this.ruleTreeView.DrawMode = System.Windows.Forms.TreeViewDrawMode.OwnerDrawText; + this.ruleTreeView.Location = new System.Drawing.Point(15, 51); + this.ruleTreeView.Name = "ruleTreeView"; + this.ruleTreeView.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.ruleTreeView.Size = new System.Drawing.Size(377, 190); + this.ruleTreeView.TabIndex = 2; + this.ruleTreeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.RuleTreeViewAfterCheck); + this.ruleTreeView.DrawNode += new System.Windows.Forms.DrawTreeNodeEventHandler(this.RuleTreeViewDrawNode); + this.ruleTreeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.RuleTreeViewMouseDown); + // + // AnalysisProjectOptions + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.ruleTreeView); + this.Controls.Add(this.enableCheckBox); + this.Controls.Add(panel1); + this.Name = "AnalysisProjectOptions"; + this.Size = new System.Drawing.Size(395, 244); + splitContainer1.Panel1.ResumeLayout(false); + splitContainer1.Panel2.ResumeLayout(false); + splitContainer1.ResumeLayout(false); + panel1.ResumeLayout(false); + this.ResumeLayout(false); + } + private System.Windows.Forms.Label warningOrErrorLabel; + private System.Windows.Forms.TreeView ruleTreeView; + private System.Windows.Forms.Label ruleLabel; + private System.Windows.Forms.CheckBox enableCheckBox; + } +} diff --git a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs new file mode 100644 index 0000000000..f1cfa0e0f4 --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.cs @@ -0,0 +1,343 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 28.02.2006 + * Time: 15:52 + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.CodeAnalysis +{ + public partial class AnalysisProjectOptions + { + Dictionary rules = new Dictionary(); + + public AnalysisProjectOptions() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + + enableCheckBox.Text = StringParser.Parse(enableCheckBox.Text); + ruleLabel.Text = StringParser.Parse(ruleLabel.Text); + warningOrErrorLabel.Text = StringParser.Parse(warningOrErrorLabel.Text); + + ruleLabel.SizeChanged += delegate { ruleTreeView.Invalidate(); }; + } + + bool initSuccess; + + protected override void OnVisibleChanged(EventArgs e) + { + base.OnVisibleChanged(e); + + if (ruleTreeView.Nodes.Count == 0 && this.Visible) { + FxCopWrapper.GetRuleList(Callback); + if (ruleTreeView.Nodes.Count == 0) { + ruleTreeView.Nodes.Add(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.ProjectOptions.LoadingRules}")); + } + } + } + + void Callback(List ruleList) + { + if (WorkbenchSingleton.InvokeRequired) { + WorkbenchSingleton.SafeThreadAsyncCall((Action>)Callback, ruleList); + } else { + ruleTreeView.Nodes.Clear(); + if (ruleList == null || ruleList.Count == 0) { + ruleTreeView.Nodes.Add(new TreeNode(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.ProjectOptions.CannotFindFxCop}"))); + ruleTreeView.Nodes.Add(new TreeNode(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.ProjectOptions.SpecifyFxCopPath}"))); + } else { + foreach (FxCopCategory cat in ruleList) { + CategoryTreeNode catNode = new CategoryTreeNode(cat); + ruleTreeView.Nodes.Add(catNode); + foreach (RuleTreeNode ruleNode in catNode.Nodes) { + rules.Add(ruleNode.Identifier, ruleNode); + } + } + initSuccess = true; + ReadRuleString(); + } + } + } + + #region TreeView drawing + class CategoryTreeNode : TreeNode + { + internal FxCopCategory category; + + public CategoryTreeNode(FxCopCategory category) + { + this.category = category; + this.Text = category.DisplayName; + foreach (FxCopRule rule in category.Rules) { + this.Nodes.Add(new RuleTreeNode(rule)); + } + } + + internal int ErrorState { + get { + bool allWarn = true; + bool allErr = true; + foreach (RuleTreeNode tn in Nodes) { + if (tn.isError) + allWarn = false; + else + allErr = false; + } + if (allErr) + return 1; + else if (allWarn) + return 0; + else + return -1; + } + } + } + + class RuleTreeNode : TreeNode + { + internal FxCopRule rule; + internal bool isError; + + public RuleTreeNode(FxCopRule rule) + { + this.rule = rule; + this.Text = rule.DisplayName; + } + + public string Identifier { + get { + return rule.CategoryName + "#" + rule.CheckId; + } + } + } + + void RuleTreeViewDrawNode(object sender, DrawTreeNodeEventArgs e) + { + if (e.Bounds.X < 5) return; + int state; + if (e.Node is CategoryTreeNode) { + state = (e.Node as CategoryTreeNode).ErrorState; + } else if (e.Node is RuleTreeNode) { + state = (e.Node as RuleTreeNode).isError ? 1 : 0; + } else { + e.DrawDefault = true; + return; + } + e.DrawDefault = false; + if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected) { + e.Graphics.DrawString(e.Node.Text, ruleTreeView.Font, SystemBrushes.HighlightText, e.Bounds.Location); + } else { + e.Graphics.DrawString(e.Node.Text, ruleTreeView.Font, SystemBrushes.WindowText, e.Bounds.Location); + } + e.Graphics.DrawLine(SystemPens.WindowFrame, ruleLabel.Width, e.Bounds.Top, ruleLabel.Width, e.Bounds.Bottom); + if (state == 0) { + // Warning + e.Graphics.DrawIcon(SystemIcons.Warning, new Rectangle(ruleLabel.Width + 4, e.Bounds.Y, 16, 16)); + e.Graphics.DrawString(ResourceService.GetString("Global.WarningText"), ruleTreeView.Font, SystemBrushes.WindowText, ruleLabel.Width + 24, e.Bounds.Y); + } else if (state == 1) { + // Error + e.Graphics.DrawIcon(SystemIcons.Error, new Rectangle(ruleLabel.Width + 4, e.Bounds.Y, 16, 16)); + e.Graphics.DrawString(ResourceService.GetString("Global.ErrorText"), ruleTreeView.Font, SystemBrushes.WindowText, ruleLabel.Width + 24, e.Bounds.Y); + } else { + // Mixed + e.Graphics.DrawString(StringParser.Parse("${res:ICSharpCode.CodeAnalysis.ProjectOptions.WarningErrorMixed}"), + ruleTreeView.Font, SystemBrushes.WindowText, ruleLabel.Width + 24, e.Bounds.Y); + } + } + #endregion + + #region Rule String Property + string CreateRuleString() + { + StringBuilder b = new StringBuilder(); + foreach (TreeNode category in ruleTreeView.Nodes) { + foreach (RuleTreeNode rule in category.Nodes) { + if (!rule.Checked || rule.isError) { + if (b.Length > 0) + b.Append(';'); + if (rule.Checked) + b.Append('+'); + else + b.Append('-'); + if (rule.isError) + b.Append('!'); + b.Append(rule.Identifier); + } + } + } + return b.ToString(); + } + + void ReadRuleString() + { + userCheck = false; + foreach (TreeNode cat in ruleTreeView.Nodes) { + foreach (RuleTreeNode rtn in cat.Nodes) { + rtn.Checked = true; + rtn.isError = false; + } + } + foreach (string rule2 in ruleString.Split(';')) { + string rule = rule2; + if (rule.Length == 0) continue; + bool active = true; + bool error = false; + if (rule.StartsWith("-")) { + active = false; + rule = rule.Substring(1); + } else if (rule.StartsWith("+")) { + rule = rule.Substring(1); + } + if (rule.StartsWith("!")) { + error = true; + rule = rule.Substring(1); + } + RuleTreeNode ruleNode; + if (rules.TryGetValue(rule, out ruleNode)) { + ruleNode.Checked = active; + ruleNode.isError = error; + } + } + foreach (TreeNode cat in ruleTreeView.Nodes) { + bool noneChecked = true; + foreach (RuleTreeNode rtn in cat.Nodes) { + if (rtn.Checked) { + noneChecked = false; + break; + } + } + cat.Checked = !noneChecked; + } + ruleTreeView.Invalidate(); + userCheck = true; + } + + string ruleString = ""; + + public string RuleString { + get { + if (initSuccess) + return CreateRuleString(); + else + return ruleString; + } + set { + ruleString = value; + if (initSuccess) { + ReadRuleString(); + } + } + } + #endregion + + #region ConfigurationGuiBinding + public CheckBox EnableCheckBox { + get { + return enableCheckBox; + } + } + + public ConfigurationGuiBinding CreateBinding() + { + return new ConfigBinding(this); + } + + class ConfigBinding : ConfigurationGuiBinding + { + readonly AnalysisProjectOptions po; + + public ConfigBinding(AnalysisProjectOptions po) + { + this.po = po; + po.OptionChanged += delegate { + Helper.IsDirty = true; + }; + } + + public override void Load() + { + po.RuleString = Get(""); + } + + public override bool Save() + { + Set(po.RuleString); + return true; + } + } + #endregion + + public event EventHandler OptionChanged; + + protected virtual void OnOptionChanged(EventArgs e) + { + if (OptionChanged != null) { + OptionChanged(this, e); + } + } + + void RuleTreeViewMouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && e.X > ruleLabel.Width) { + TreeNode node = ruleTreeView.GetNodeAt(20, e.Y); + if (node != null) { + if (node is RuleTreeNode) { + ((RuleTreeNode)node).isError = !((RuleTreeNode)node).isError; + } else if (node is CategoryTreeNode) { + if ((node as CategoryTreeNode).ErrorState == 0) { + foreach (RuleTreeNode rtn in node.Nodes) { + rtn.isError = true; + } + } else { + foreach (RuleTreeNode rtn in node.Nodes) { + rtn.isError = false; + } + } + } + ruleTreeView.Invalidate(); + OnOptionChanged(EventArgs.Empty); + } + } + } + + bool userCheck; + + void RuleTreeViewAfterCheck(object sender, TreeViewEventArgs e) + { + if (userCheck) { + if (e.Node is CategoryTreeNode) { + userCheck = false; + foreach (TreeNode subNode in e.Node.Nodes) { + subNode.Checked = e.Node.Checked; + } + userCheck = true; + } else if (e.Node is RuleTreeNode) { + userCheck = false; + bool anyChecked = false; + foreach (TreeNode sibling in e.Node.Parent.Nodes) { + if (sibling.Checked) + anyChecked = true; + } + e.Node.Parent.Checked = anyChecked; + userCheck = true; + } + OnOptionChanged(EventArgs.Empty); + } + } + } +} diff --git a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.resx b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.resx new file mode 100644 index 0000000000..a98b238dd9 --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptions.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + \ No newline at end of file diff --git a/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptionsPanel.cs b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptionsPanel.cs new file mode 100644 index 0000000000..83e65765e5 --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/AnalysisProjectOptionsPanel.cs @@ -0,0 +1,40 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 28.02.2006 + * Time: 15:29 + */ + +using System; +using System.Drawing; +using System.Windows.Forms; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.CodeAnalysis +{ + public class AnalysisProjectOptionsPanel : AbstractProjectOptionPanel + { + public override void LoadPanelContents() + { + InitializeHelper(); + + AnalysisProjectOptions po = new AnalysisProjectOptions(); + po.Dock = DockStyle.Fill; + Controls.Add(po); + + ChooseStorageLocationButton btn; + btn = helper.BindBoolean(po.EnableCheckBox, "RunCodeAnalysis", false).CreateLocationButton(po.EnableCheckBox); + ConfigurationGuiBinding binding = po.CreateBinding(); + binding.RegisterLocationButton(btn); + helper.AddBinding("CodeAnalysisRules", binding); + + Control ctl = helper.CreateConfigurationSelector(); + ctl.Dock = DockStyle.Top; + Controls.Add(ctl); + } + } +} diff --git a/src/AddIns/Misc/CodeAnalysis/Src/FxCopRule.cs b/src/AddIns/Misc/CodeAnalysis/Src/FxCopRule.cs new file mode 100644 index 0000000000..c94661329d --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/FxCopRule.cs @@ -0,0 +1,112 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 28.02.2006 + * Time: 17:16 + */ + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.CodeAnalysis +{ + public class FxCopRule : IComparable + { + readonly string checkId; + readonly string displayName; + readonly string categoryName; + readonly string description; + readonly string url; + + public FxCopRule(string checkId, string displayName, string categoryName, string description, string url) + { + this.checkId = checkId; + this.displayName = displayName; + this.categoryName = categoryName; + this.description = description; + this.url = url; + } + + public string CheckId { + get { + return checkId; + } + } + + public string DisplayName { + get { + return displayName; + } + } + + public string CategoryName { + get { + return categoryName; + } + } + + public string Description { + get { + return description; + } + } + + public string Url { + get { + return url; + } + } + + public override string ToString() + { + return string.Format("[FxCopRule {0}#{1}]", this.categoryName, this.checkId); + } + + public int CompareTo(object obj) + { + FxCopRule o = (FxCopRule)obj; + int r = categoryName.CompareTo(o.categoryName); + if (r != 0) return r; + return displayName.CompareTo(o.displayName); + } + } + + public class FxCopCategory + { + readonly string name; + readonly string displayName; + readonly List rules = new List(); + + public FxCopCategory(string name) + { + this.name = name; + if (name.StartsWith("Microsoft.")) + displayName = name.Substring(10); + else + displayName = name; + } + + public string Name { + get { + return name; + } + } + + public string DisplayName { + get { + return displayName; + } + } + + public List Rules { + get { + return rules; + } + } + + public override string ToString() + { + return string.Format("[FxCopCategory {0}]", this.name); + } + } +} diff --git a/src/AddIns/Misc/CodeAnalysis/Src/FxCopWrapper.cs b/src/AddIns/Misc/CodeAnalysis/Src/FxCopWrapper.cs new file mode 100644 index 0000000000..23104bf69e --- /dev/null +++ b/src/AddIns/Misc/CodeAnalysis/Src/FxCopWrapper.cs @@ -0,0 +1,203 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 28.02.2006 + * Time: 16:24 + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Microsoft.Win32; +using ICSharpCode.Core; + +namespace ICSharpCode.CodeAnalysis +{ + public static class FxCopWrapper + { + static List rules = new List(); + static List>> callbacks = new List>>(); + + /// + /// Gets the rules supported by the current FxCop version. The rules are loaded on a separate + /// thread, the callback is fired when the action has completed. + /// Warning: the callback might be fired on the current thread if the rules are already loaded, + /// or on another thread! + /// + public static void GetRuleList(Action> callback) + { + int count; + lock (rules) { + count = rules.Count; + if (count == 0) { + callbacks.Add(callback); + if (callbacks.Count == 1) { + // Start the thread: + System.Threading.ThreadPool.QueueUserWorkItem(RunGetRuleList); + } + } + } + if (count > 0) { + callback(rules); + } + } + + public static string FindFxCopPath() + { + string fxCopPath = PropertyService.Get("CodeAnalysis.FxCopPath"); + if (fxCopPath.Length > 0 && File.Exists(Path.Combine(fxCopPath, "FxCopCommon.dll"))) { + return fxCopPath; + } + fxCopPath = FromRegistry(Registry.CurrentUser.OpenSubKey(@"Software\Classes\FxCopProject\Shell\Open\Command")); + if (fxCopPath.Length > 0 && File.Exists(Path.Combine(fxCopPath, "FxCopCommon.dll"))) { + return fxCopPath; + } + fxCopPath = FromRegistry(Registry.ClassesRoot.OpenSubKey(@"FxCopProject\Shell\Open\Command")); + if (fxCopPath.Length > 0 && File.Exists(Path.Combine(fxCopPath, "FxCopCommon.dll"))) { + return fxCopPath; + } + return null; + } + + static string FromRegistry(RegistryKey key) + { + if (key == null) return string.Empty; + using (key) { + string cmd = key.GetValue("").ToString(); + int pos; + if (cmd.StartsWith("\"")) + pos = cmd.IndexOf('"', 1); + else + pos = cmd.IndexOf(' '); + try { + if (cmd.StartsWith("\"")) + return Path.GetDirectoryName(cmd.Substring(1, pos - 1)); + else + return Path.GetDirectoryName(cmd.Substring(0, pos)); + } catch (ArgumentException ex) { + LoggingService.Warn(cmd); + LoggingService.Warn(ex); + return string.Empty; + } + } + } + + static void RunGetRuleList(object state) + { + LoggingService.Debug("Trying to find FxCop rules"); + string fxCopPath = FindFxCopPath(); + if (fxCopPath != null) { + try { + GetRuleListAndSort(fxCopPath); + } catch (Exception ex) { + LoggingService.Warn(ex); + } + } + Action>[] callbacks_tmp; + lock (rules) { + callbacks_tmp = callbacks.ToArray(); + callbacks.Clear(); + } + LoggingService.Debug("Finished getting FxCop rules, invoking " + callbacks_tmp.Length + " callback"); + foreach (Action> callback in callbacks_tmp) { + callback(rules); + } + } + + static void GetRuleListAndSort(string fxCopPath) + { + AppDomainSetup setup = new AppDomainSetup(); + setup.DisallowCodeDownload = true; + setup.ApplicationBase = fxCopPath; + AppDomain domain = AppDomain.CreateDomain("FxCop Rule Loading Domain", AppDomain.CurrentDomain.Evidence, setup); + + string[][] ruleTextList; + try { + ruleTextList = (string[][])AppDomainLaunchHelper.LaunchInAppDomain(domain, typeof(FxCopWrapper), "GetRuleListInCurrentAppDomain", fxCopPath); + } finally { + AppDomain.Unload(domain); + } + + FxCopRule[] ruleList = new FxCopRule[ruleTextList.Length]; + for (int i = 0; i < ruleTextList.Length; i++) { + ruleList[i] = new FxCopRule(ruleTextList[i][0], ruleTextList[i][1], + ruleTextList[i][2], ruleTextList[i][3], + ruleTextList[i][4]); + } + + Array.Sort(ruleList); + lock (rules) { + FxCopCategory cat = null; + foreach (FxCopRule rule in ruleList) { + if (cat == null || cat.Name != rule.CategoryName) { + cat = new FxCopCategory(rule.CategoryName); + rules.Add(cat); + } + cat.Rules.Add(rule); + } + } + } + + // We don't want to reference the FxCop assembly + + static object CallMethod(object instance, string name, params object[] args) + { + return instance.GetType().InvokeMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, instance, args); + } + + static object CallMethod(Type type, string name, BindingFlags flags, object instance, params object[] args) + { + return type.InvokeMember(name, flags | BindingFlags.Public | BindingFlags.InvokeMethod, + null, instance, args); + } + + static object GetProp(object instance, string name) + { + return instance.GetType().InvokeMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty, + null, instance, null); + } + + static string GetSProp(object instance, string name) + { + object v = GetProp(instance, name); + if (v == null) + return string.Empty; + else + return v.ToString(); + } + + public static string[][] GetRuleListInCurrentAppDomain(string fxCopPath) + { + Assembly asm = Assembly.LoadFrom(Path.Combine(fxCopPath, "FxCopCommon.dll")); + + Type fxCopOM = asm.GetType("Microsoft.FxCop.Common.FxCopOM"); + CallMethod(fxCopOM, "Initialize", BindingFlags.Static, null); + + object project = asm.CreateInstance("Microsoft.FxCop.Common.Project"); + fxCopOM.InvokeMember("Project", BindingFlags.Public | BindingFlags.Static | BindingFlags.SetProperty, + null, null, new object[] { project }); + + object exceptionList = CallMethod(project, "Initialize"); + foreach (Exception ex in ((IEnumerable)exceptionList)) { + LoggingService.Warn(ex); + } + + IEnumerable ruleList = (IEnumerable)GetProp(GetProp(project, "AllRules"), "Values"); + List rules = new List(); + foreach (object ruleContainer in ruleList) { + object rule = GetProp(ruleContainer, "IRule"); + rules.Add(new string[] { + GetSProp(rule, "CheckId"), + GetSProp(rule, "Name"), + GetSProp(rule, "Category"), + GetSProp(rule, "Description"), + GetSProp(rule, "Url") + }); + } + + return rules.ToArray(); + } + } +} diff --git a/src/Main/Core/Project/ICSharpCode.Core.csproj b/src/Main/Core/Project/ICSharpCode.Core.csproj index 866780f348..827e69892e 100644 --- a/src/Main/Core/Project/ICSharpCode.Core.csproj +++ b/src/Main/Core/Project/ICSharpCode.Core.csproj @@ -148,6 +148,7 @@ Configuration\GlobalAssemblyInfo.cs + diff --git a/src/Main/Core/Project/Src/Util/AppDomainLaunchHelper.cs b/src/Main/Core/Project/Src/Util/AppDomainLaunchHelper.cs new file mode 100644 index 0000000000..c85f8a6b0d --- /dev/null +++ b/src/Main/Core/Project/Src/Util/AppDomainLaunchHelper.cs @@ -0,0 +1,51 @@ +/* + * Created by SharpDevelop. + * User: Daniel Grunwald + * Date: 28.02.2006 + * Time: 18:03 + */ + +using System; +using System.Reflection; + +namespace ICSharpCode.Core +{ + /// + /// Because AddIn assemblies are loaded into the LoadFrom context, creating AppDomains in them that + /// use an arbitrary ApplicationBase path does not work correctly. + /// This class contains a static method that helps launching a static method on a type in a new AppDomain. + /// + /// + /// + /// public static class CurrentClass { // is NOT MarshalByRef + /// public static ResultClass[] GetResults() + /// { + /// AppDomainSetup setup = new AppDomainSetup(); + /// setup.ApplicationBase = myApplicationBase; + /// AppDomain domain = AppDomain.CreateDomain("Display name for domain", AppDomain.CurrentDomain.Evidence, setup); + /// try { + /// return (ResultClass[])AppDomainLaunchHelper.LaunchInAppDomain(domain, typeof(CurrentClass), "GetResultsDirectly", requestObject); + /// } finally { + /// AppDomain.Unload(domain); + /// } + /// } + /// public static ResultClass[] GetResultsDirectly(Request requestObject) { ... } + /// } + /// [Serializable] class Request { ... } // must be serializable !!! + /// [Serializable] class ResultClass { ... } // must be serializable !!! + /// + public class AppDomainLaunchHelper : MarshalByRefObject + { + public object LaunchMethod(string assemblyFile, string typeName, string methodName, object[] arguments) + { + Type t = Assembly.LoadFrom(assemblyFile).GetType(typeName); + return t.InvokeMember(methodName, (BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod), null, null, arguments); + } + + public static object LaunchInAppDomain(AppDomain domain, Type type, string methodName, params object[] arguments) + { + AppDomainLaunchHelper h = (AppDomainLaunchHelper)domain.CreateInstanceFromAndUnwrap(typeof(AppDomainLaunchHelper).Assembly.Location, typeof(AppDomainLaunchHelper).FullName); + return h.LaunchMethod(type.Assembly.Location, type.FullName, methodName, arguments); + } + } +} diff --git a/src/SharpDevelop.sln b/src/SharpDevelop.sln index 6b566a102c..e4dd9694b1 100644 --- a/src/SharpDevelop.sln +++ b/src/SharpDevelop.sln @@ -1,5 +1,5 @@ Microsoft Visual Studio Solution File, Format Version 9.00 -# SharpDevelop 2.0.0.1128 +# SharpDevelop 2.1.0.1190 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{14A277EE-7DF1-4529-B639-7D1EF334C1C5}" ProjectSection(SolutionItems) = postProject EndProjectSection @@ -46,6 +46,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{CE5B42B7-6 ProjectSection(SolutionItems) = postProject EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis", "AddIns\Misc\CodeAnalysis\CodeAnalysis.csproj", "{3EAA45A9-735C-4AC7-A799-947B93EA449D}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubversionAddIn", "AddIns\Misc\SubversionAddIn\Project\SubversionAddIn.csproj", "{17F4D7E0-6933-4C2E-8714-FD7E98D625D5}" EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HtmlHelp2JScriptGlobals", "AddIns\Misc\HtmlHelp2\JScriptGlobals\HtmlHelp2JScriptGlobals.vbproj", "{E54A5AD2-418D-4A85-BA5E-CD803DE38715}" @@ -282,6 +284,10 @@ Global {17F4D7E0-6933-4C2E-8714-FD7E98D625D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17F4D7E0-6933-4C2E-8714-FD7E98D625D5}.Release|Any CPU.Build.0 = Release|Any CPU {17F4D7E0-6933-4C2E-8714-FD7E98D625D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EAA45A9-735C-4AC7-A799-947B93EA449D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EAA45A9-735C-4AC7-A799-947B93EA449D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EAA45A9-735C-4AC7-A799-947B93EA449D}.Release|Any CPU.Build.0 = Release|Any CPU + {3EAA45A9-735C-4AC7-A799-947B93EA449D}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} = {14A277EE-7DF1-4529-B639-7D1EF334C1C5} @@ -318,6 +324,7 @@ Global {1F261725-6318-4434-A1B1-6C70CE4CD324} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} {E54A5AD2-418D-4A85-BA5E-CD803DE38715} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} {17F4D7E0-6933-4C2E-8714-FD7E98D625D5} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} + {3EAA45A9-735C-4AC7-A799-947B93EA449D} = {CE5B42B7-6E8C-4385-9E97-F4023FC16BF2} {B08385CD-F0CC-488C-B4F4-EEB34B6D2688} = {6604365C-C702-4C10-9BA8-637F1E3D4D0D} {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {6604365C-C702-4C10-9BA8-637F1E3D4D0D} {EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {6604365C-C702-4C10-9BA8-637F1E3D4D0D}