-
Notifications
You must be signed in to change notification settings - Fork 4k
Adding static analysis tool with dependency checker rules #1755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
27b408b
Adding static analysis tool with dependency checker rules
markcowl c08cd34
responding to review feedback
markcowl b1e1f54
adding null checks to AssemblyMetadata and Decorator constructors
markcowl e5f95e7
Adding header to assemblyinfo
markcowl f03c2ef
fixing line endings
markcowl 0817d6d
More line endings fixes
markcowl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// ---------------------------------------------------------------------------------- | ||
// | ||
// Copyright Microsoft Corporation | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// ---------------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace StaticAnalysis | ||
{ | ||
/// <summary> | ||
/// Abstract class to implement the report logging structure | ||
/// </summary> | ||
public abstract class AnalysisLogger | ||
{ | ||
string _baseDirectory; | ||
|
||
/// <summary> | ||
/// Create an analysis logger that will write reports to the given directory | ||
/// </summary> | ||
/// <param name="baseDirectory"></param> | ||
public AnalysisLogger(string baseDirectory) | ||
{ | ||
_baseDirectory = baseDirectory; | ||
} | ||
|
||
IList<ReportLogger> _loggers = new List<ReportLogger>(); | ||
protected virtual IList<ReportLogger> Loggers { get { return _loggers; } } | ||
|
||
/// <summary> | ||
/// Write an error report. | ||
/// </summary> | ||
/// <param name="error">The message to write</param> | ||
public abstract void WriteError(string error); | ||
|
||
public virtual void WriteError(string format, params object[] args) | ||
{ | ||
WriteError(string.Format(format, args)); | ||
} | ||
|
||
/// <summary> | ||
/// Write an informational message. | ||
/// </summary> | ||
/// <param name="message">The message to write</param> | ||
public abstract void WriteMessage(string message); | ||
|
||
public virtual void WriteMessage(string format, params object[] args) | ||
{ | ||
WriteMessage(string.Format(format, args)); | ||
} | ||
|
||
/// <summary> | ||
/// Write a warning. | ||
/// </summary> | ||
/// <param name="message">The warning text</param> | ||
public abstract void WriteWarning(string message); | ||
|
||
public virtual void WriteWarning(string format, params object[] args) | ||
{ | ||
WriteWarning(string.Format(format, args)); | ||
} | ||
|
||
/// <summary> | ||
/// Write a report file to the given file, using the given file contents. | ||
/// </summary> | ||
/// <param name="name">The path to the file</param> | ||
/// <param name="contents">The contents of the report</param> | ||
public abstract void WriteReport(string name, string contents); | ||
|
||
/// <summary> | ||
/// Create a logger for a particular report | ||
/// </summary> | ||
/// <typeparam name="T">The type of records written to the log</typeparam> | ||
/// <param name="fileName">The filename (without file path) where the report will be written</param> | ||
/// <returns>The given logger. Analyzer may write records to this logger and they will be written to | ||
/// the report file.</returns> | ||
public virtual ReportLogger<T> CreateLogger<T>(string fileName) where T : IReportRecord, new() | ||
{ | ||
if (string.IsNullOrWhiteSpace(fileName)) | ||
{ | ||
throw new ArgumentNullException("fileName"); | ||
} | ||
|
||
var filePath = Path.Combine(_baseDirectory, fileName); | ||
var logger = new ReportLogger<T>(filePath, this); | ||
Loggers.Add(logger); | ||
return logger; | ||
} | ||
|
||
/// <summary> | ||
/// Write out the report files for each of the added report loggers. | ||
/// </summary> | ||
public void WriteReports() | ||
{ | ||
foreach (var logger in Loggers.Where(l => l.Records.Any())) | ||
{ | ||
StringBuilder reportText = new StringBuilder(); | ||
reportText.AppendLine(logger.Records.First().PrintHeaders()); | ||
foreach (var reportRecord in logger.Records) | ||
{ | ||
reportText.AppendLine(reportRecord.FormatRecord()); | ||
} | ||
|
||
WriteReport(logger.FileName, reportText.ToString()); | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// ---------------------------------------------------------------------------------- | ||
// | ||
// Copyright Microsoft Corporation | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// ---------------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.IO; | ||
|
||
namespace StaticAnalysis | ||
{ | ||
/// <summary> | ||
/// Simple class for logging errors and warnings to the console and writing reports to the file system. | ||
/// </summary> | ||
public class ConsoleLogger : AnalysisLogger | ||
{ | ||
|
||
public ConsoleLogger(string baseDirectory) | ||
: base(baseDirectory) | ||
{ | ||
} | ||
|
||
public override void WriteError(string error) | ||
{ | ||
Console.WriteLine("### ERROR {0}", error); | ||
} | ||
|
||
public override void WriteMessage(string message) | ||
{ | ||
Console.WriteLine(message); | ||
} | ||
|
||
public override void WriteWarning(string message) | ||
{ | ||
Console.WriteLine("Warning: {0}", message); | ||
} | ||
|
||
public override void WriteReport(string name, string records) | ||
{ | ||
File.WriteAllText(name, records); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// ---------------------------------------------------------------------------------- | ||
// | ||
// Copyright Microsoft Corporation | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// ---------------------------------------------------------------------------------- | ||
|
||
using System; | ||
|
||
namespace StaticAnalysis | ||
{ | ||
/// <summary> | ||
/// Abstract class to implement the Decorator pattern | ||
/// </summary> | ||
/// <typeparam name="T"></typeparam> | ||
public class Decorator<T> | ||
{ | ||
Action<T> _action; | ||
string _name; | ||
|
||
protected Decorator(Action<T> action, string name) | ||
{ | ||
_action = action; | ||
_name = name; | ||
Inner = null; | ||
} | ||
|
||
public static Decorator<T> Create() | ||
{ | ||
return new Decorator<T>(r => { }, "default"); | ||
} | ||
|
||
public void Apply(T record) | ||
{ | ||
_action(record); | ||
if (Inner != null) | ||
{ | ||
Inner.Apply(record); | ||
} | ||
} | ||
|
||
public void AddDecorator(Action<T> action, string name) | ||
{ | ||
if (action == null) | ||
{ | ||
throw new ArgumentNullException("action"); | ||
} | ||
|
||
if (name == null) | ||
{ | ||
throw new ArgumentNullException("name"); | ||
} | ||
|
||
if (Inner == null) | ||
{ | ||
Inner = new Decorator<T>(action, name); | ||
} | ||
else | ||
{ | ||
Inner.AddDecorator(action, name); | ||
} | ||
} | ||
|
||
public void Remove(string name) | ||
{ | ||
if (Inner != null) | ||
{ | ||
if (string.Equals(Inner._name, name)) | ||
{ | ||
Inner = Inner.Inner; | ||
} | ||
else | ||
{ | ||
Inner.Remove(name); | ||
} | ||
} | ||
} | ||
|
||
protected Decorator<T> Inner { get; set; } | ||
} | ||
} |
101 changes: 101 additions & 0 deletions
101
tools/StaticAnalysis/DependencyAnalyzer/AssemblyLoader.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// ---------------------------------------------------------------------------------- | ||
// | ||
// Copyright Microsoft Corporation | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// ---------------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Reflection; | ||
|
||
namespace StaticAnalysis.DependencyAnalyzer | ||
{ | ||
/// <summary> | ||
/// A class using .Net Remoting to load assemblies and retrieve information in a separate app domain | ||
/// </summary> | ||
public class AssemblyLoader : MarshalByRefObject | ||
{ | ||
/// <summary> | ||
/// Load the assembly in the reflection context by name. Will succeed if the referenced assembly name can | ||
/// be found using default assembly loading rules (i.e. it is in the current directory or the GAC) | ||
/// </summary> | ||
/// <param name="assemblyName">The full name of the assembly</param> | ||
/// <returns>Information on the given assembly, if it was loaded successfully, or null if there is an | ||
/// assembly loading issue. </returns> | ||
public AssemblyMetadata GetReflectedAssemblyInfo(string assemblyName) | ||
{ | ||
if (string.IsNullOrWhiteSpace(assemblyName)) | ||
{ | ||
throw new ArgumentException("assemblyName"); | ||
} | ||
|
||
AssemblyMetadata result = null; | ||
try | ||
{ | ||
result = new AssemblyMetadata(Assembly.ReflectionOnlyLoad(assemblyName)); | ||
} | ||
catch | ||
{ | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Load the assembly found at the given path in the reflection context and return assembly metadata | ||
/// </summary> | ||
/// <param name="assemblyPath">The full path to the assembly file.</param> | ||
/// <returns>Assembly metadata if the assembly is loaded successfully, or null if there are load errors.</returns> | ||
public AssemblyMetadata GetReflectedAssemblyFromFile(string assemblyPath) | ||
{ | ||
if (string.IsNullOrWhiteSpace(assemblyPath)) | ||
{ | ||
throw new ArgumentException("assemblyPath"); | ||
} | ||
|
||
AssemblyMetadata result = null; | ||
try | ||
{ | ||
return new AssemblyMetadata(Assembly.ReflectionOnlyLoadFrom(assemblyPath)); | ||
} | ||
catch | ||
{ | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Create a new AppDomain and create a remote instance of AssemblyLoader we can use there | ||
/// </summary> | ||
/// <param name="directoryPath">directory containing assemblies</param> | ||
/// <param name="testDomain">A new AppDomain, where assemblies can be loaded</param> | ||
/// <returns>A proxy to the AssemblyLoader running in the newly created app domain</returns> | ||
public static AssemblyLoader Create(string directoryPath, out AppDomain testDomain) | ||
{ | ||
if (string.IsNullOrWhiteSpace(directoryPath)) | ||
{ | ||
throw new ArgumentException("directoryPath"); | ||
} | ||
|
||
var setup = new AppDomainSetup(); | ||
setup.ApplicationBase = directoryPath; | ||
setup.ApplicationName = "TestDomain"; | ||
setup.ApplicationTrust = AppDomain.CurrentDomain.ApplicationTrust; | ||
setup.DisallowApplicationBaseProbing = false; | ||
setup.DisallowCodeDownload = false; | ||
setup.DisallowBindingRedirects = false; | ||
setup.DisallowPublisherPolicy = false; | ||
testDomain = AppDomain.CreateDomain("TestDomain", null, setup); | ||
return testDomain.CreateInstanceFromAndUnwrap(typeof(AssemblyLoader).Assembly.Location, | ||
typeof(AssemblyLoader).FullName) as AssemblyLoader; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a null check for parameters either here or in ctor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think this is a good practice in general