-
Notifications
You must be signed in to change notification settings - Fork 231
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
Fix S3881: Rule throws AD0001 with SyntaxTree not part of the compilation #1804
Comments
Hi @jcurl, Could you let us know the version of SonarC# you are using? Copy-pasting your class and using the latest NuGet, I cannot reproduce the issue. |
Hello, the version as given by the Visual Studio extensions manager is in the problem description as SonarC# Version VS Extension 4.3.0.3718 |
If it helps, the target .NET framework is 4.5, and so the NuGet package I'm using is the latest for .NET 4.5 (I can't use the newest as they target .NET 4.6, etc.) |
On another computer where SonarLint is C# 4.2.0.3692, the AD001 doesn't appear to occur. I upgraded to 4.4.0.x and the error is still there. Severity Code Description Project File Line Suppression State
Warning AD0001 Analyzer 'SonarAnalyzer.Rules.CSharp.ImplementIDisposableCorrectly' threw an exception of type 'System.ArgumentException' with message 'SyntaxTree 'namespace RJCP.RjBuild.Infrastructure.Collections.Generic
{
using System;
using System.Collections.Generic;
/// <summary>
/// A collection of items which are named and then indexed in a dictionary by name.
/// </summary>
/// <typeparam name="T">The type of the object which this collection manages.</typeparam>
/// <remarks>
/// By having a base class implementation for managing the collection, we present a consistent
/// interface to the user application.
/// </remarks>
public abstract class NamedItemCollection<T> : IDisposable, ICollection<T> where T : class, INamedItem
{
/// <summary>
/// Initializes a new instance of the <see cref="NamedItemCollection{T}"/> class without a <see cref="Name"/>.
/// </summary>
/// <remarks>
/// This constructor is intended for collections that need to be able to set the name during the constructor,
/// instead of prior (for example, a file might need to be parsed to determine the name). The name should be
/// set once the construction of your object is complete.
/// </remarks>
protected NamedItemCollection() { }
/// <summary>
/// Initializes a new instance of the <see cref="NamedItemCollection{T}"/> class.
/// </summary>
/// <param name="name">The name of the objects that this instance manages.</param>
/// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <see langword="null"/>.</exception>
protected NamedItemCollection(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
Name = name;
}
private string m_Name;
/// <summary>
/// Gets the name of this collection.
/// </summary>
/// <value>
/// The name of this collection.
/// </value>
public string Name
{
get
{
if (m_Name == null) {
return GetType().ToString();
}
return m_Name;
}
protected set
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (m_Name != null) throw new InvalidOperationException(Resources.Infra_Collections_NamedItem_NameLocked);
m_Name = value;
}
}
private Dictionary<string, T> m_Items = new Dictionary<string, T>();
/// <summary>
/// Gets the item of the specified device name with <paramref name="key"/>.
/// </summary>
/// <value>
/// The requested item. If the item cannot be found, <see langword="null"/> can be returned.
/// </value>
/// <param name="key">The item name.</param>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <exception cref="ArgumentNullException"><paramref name="key"/> may not be <see langword="null"/>.</exception>
public T this[string key]
{
get
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (key == null) throw new ArgumentNullException(nameof(key));
T item;
if (m_Items.TryGetValue(key, out item)) return item;
string message = string.Format(Resources.Infra_Collections_NamedItem_NotFound, key, Name);
throw new ArgumentException(message);
}
}
/// <summary>
/// Attempts to get the item for the specified key.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="item">The item.</param>
/// <returns><see langword="true"/> if the item with the <paramref name="key"/> exists in the collection and so
/// is returned, <see langword="false"/> if the <paramref name="key"/> doesn't exist and so <paramref name="item"/>
/// is undefined.</returns>
/// <exception cref="System.ObjectDisposedException"></exception>
/// <exception cref="System.ArgumentNullException">key</exception>
public bool TryGetValue(string key, out T item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (key == null) throw new ArgumentNullException(nameof(key));
return (m_Items.TryGetValue(key, out item));
}
#region IEnumerable<T>
/// <summary>
/// Returns an enumerator that iterates through the collection of items.
/// </summary>
/// <returns>
/// An enumerator that can be used to iterate through the collection of items.
/// </returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
public IEnumerator<T> GetEnumerator()
{
if (IsDisposed) throw new ObjectDisposedException(Name);
return m_Items.Values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region ICollection<T>
/// <summary>
/// Adds an item to this collection.
/// </summary>
/// <param name="item">The item to add to the collection.</param>
/// <exception cref="System.InvalidOperationException">Collection is read only.</exception>
/// <exception cref="System.ArgumentNullException"><paramref name="item"/> may not be <see langword="null"/>.</exception>
/// <exception cref="System.ArgumentException">Invalid <paramref name="item"/> name;
/// or an <paramref name="item"/> with same name already exists in the collection.</exception>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <remarks>
/// It is expected that the name of the item does not change after being added. Doing so may result
/// in unexpected behavior.
/// </remarks>
public void Add(T item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
OnAdd(item);
InternalAdd(item);
try {
OnAddComplete(item);
} catch {
m_Items.Remove(item.Name);
throw;
}
}
/// <summary>
/// Adds a range of items to the collection.
/// </summary>
/// <param name="items">The items that should be added.</param>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <exception cref="System.InvalidOperationException">Collection is read only.</exception>
/// <exception cref="System.ArgumentNullException">An element in <paramref name="items"/> may not be <see langword="null"/>.</exception>
/// <exception cref="System.ArgumentException">An invalid element name in <paramref name="items"/>;
/// or an element in <paramref name="items"/> with same name already exists in the collection.</exception>
public void AddRange(IEnumerable<T> items)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
foreach (T item in items) {
OnAdd(item);
InternalAddCheck(item);
}
foreach (T item in items) {
m_Items.Add(item.Name, item);
try {
OnAddComplete(item);
} catch {
m_Items.Remove(item.Name);
throw;
}
}
}
private void InternalAddCheck(T item)
{
if (IsReadOnly) throw new InvalidOperationException(Resources.Infra_Collections_ReadOnly);
if (item == null) throw new ArgumentNullException(nameof(item));
if (string.IsNullOrWhiteSpace(item.Name)) throw new ArgumentException(Resources.Infra_Collections_NamedItem_InvalidName, nameof(item));
if (m_Items.ContainsKey(item.Name)) {
string message = string.Format(Resources.Infra_Collections_NamedItem_Duplicate, item.Name);
throw new ArgumentException(message);
}
}
/// <summary>
/// Adds the item to the collection directly without raising events.
/// </summary>
/// <param name="item">The item that should be added.</param>
protected void InternalAdd(T item)
{
InternalAddCheck(item);
m_Items.Add(item.Name, item);
}
/// <summary>
/// A derived class can override this method to perform additional checks on the item before it is added.
/// </summary>
/// <param name="item">The item to add.</param>
/// <remarks>
/// Implementor should override this method and raise an exception to prevent the item from being added.
/// </remarks>
protected virtual void OnAdd(T item) { }
/// <summary>
/// Called after the item is added tot he collection.
/// </summary>
/// <param name="item">The item that was added to the collection.</param>
protected virtual void OnAddComplete(T item) { }
/// <summary>
/// Removes all items from this collection.
/// </summary>
/// <exception cref="System.InvalidOperationException">Collection is read only.</exception>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <remarks>
/// Removing all items from this collection doesn't dispose the items in the collection. You should either
/// <see cref="Dispose()"/> this collection or dispose the objects individually if required.
/// </remarks>
public void Clear()
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (IsReadOnly) throw new InvalidOperationException(Resources.Infra_Collections_ReadOnly);
OnClear();
InternalClear();
OnClearComplete();
}
/// <summary>
/// Clears the internal collection without raising events.
/// </summary>
protected void InternalClear()
{
m_Items.Clear();
}
/// <summary>
/// Called before the collection is about to be cleared.
/// </summary>
/// <remarks>
/// If there is an error that should prevent the collection being cleared, an exception should be raised here.
/// </remarks>
protected virtual void OnClear() { }
/// <summary>
/// Called after the internal collection is cleared.
/// </summary>
protected virtual void OnClearComplete() { }
/// <summary>
/// Determines whether the collection contains an item of the specified name.
/// </summary>
/// <param name="item">The item name to check for.</param>
/// <returns><see langword="true"/> if the collection contains an item with the name specified.</returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <remarks>
/// This method returns <see langword="false"/> if no element with the keyword exists. If <paramref name="item"/>
/// is <see langword="null"/>, then <see langword="false"/> is returned.
/// </remarks>
public bool Contains(string item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (item == null) return false;
return m_Items.ContainsKey(item);
}
/// <summary>
/// Determines whether the collection contains an item of the same name.
/// </summary>
/// <param name="item">The item whose name to locate in the collection.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="item" /> is found in the collection; otherwise, <see langword="false"/>.
/// </returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <exception cref="ArgumentNullException"><paramref name="item"/> may not be <see langword="null"/>.</exception>
public bool Contains(T item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (item == null) throw new ArgumentNullException(nameof(item));
return m_Items.ContainsValue(item);
}
/// <summary>
/// Copies the collection of items in the collection to the specified array.
/// </summary>
/// <param name="array">The array to copy the collection to.</param>
/// <param name="arrayIndex">Index in the array where to start copying to.</param>
/// <exception cref="ArgumentNullException"><paramref name="array"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than zero.</exception>
/// <exception cref="ArgumentException"><paramref name="array"/> is multidimensional; or the number of elements in
/// the collection is greater than the available space from <paramref name="arrayIndex"/> to the
/// end of the destination <paramref name="array"/>; or the collection cannot be automatically
/// cast to the type of the destination <paramref name="array"/>.</exception>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
public void CopyTo(T[] array, int arrayIndex)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
m_Items.Values.CopyTo(array, arrayIndex);
}
/// <summary>
/// Gets the number of elements contained in the collection.
/// </summary>
/// <returns>The number of elements contained in the collection.</returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
public int Count
{
get
{
if (IsDisposed) throw new ObjectDisposedException(Name);
return m_Items.Count;
}
}
private bool m_IsReadOnly;
/// <summary>
/// Gets a value indicating whether the collection is read-only.
/// </summary>
/// <exception cref="System.InvalidOperationException">Collection is read only and cannot change this property.</exception>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <returns><see langword="true"/> if the collection is read-only; otherwise, <see langword="false"/>.</returns>
/// <remarks>
/// Initially, the collection is read/write. You may write to this property to set the collection to read-only,
/// which the collection can never be made read/write again.
/// </remarks>
public bool IsReadOnly
{
get { return m_IsReadOnly; }
set
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (m_IsReadOnly != value) {
if (m_IsReadOnly) throw new InvalidOperationException(Resources.Infra_Collections_ReadOnly);
m_IsReadOnly = value;
}
}
}
/// <summary>
/// Removes the occurrence of the item from the collection.
/// </summary>
/// <param name="item">The item to remove from the collection.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="item" /> was successfully removed from the collection;
/// otherwise, <see langword="false"/>. This method also returns <see langword="false"/> if <paramref name="item" /> is not found
/// in the collection.
/// </returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <exception cref="System.InvalidOperationException">Collection is read only.</exception>
/// <exception cref="System.ArgumentNullException"><paramref name="item"/> may not be <see langword="null"/>.</exception>
/// <remarks>
/// Removing an item from the collection doesn't dispose the item being removed from the collection.
/// </remarks>
public bool Remove(T item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (item == null) throw new ArgumentNullException(nameof(item));
if (IsReadOnly) throw new InvalidOperationException(Resources.Infra_Collections_ReadOnly);
if (!Contains(item)) return false;
return InternalRemoveWithChecks(item.Name, item);
}
/// <summary>
/// Removes the occurrence of the item having the specified name from the collection.
/// </summary>
/// <param name="item">The item name to remove from the collection.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="item" /> was successfully removed from the collection;
/// otherwise, <see langword="false"/>. This method also returns <see langword="false"/> if <paramref name="item" /> is not found
/// in the collection.
/// </returns>
/// <exception cref="System.ObjectDisposedException">Object is already disposed.</exception>
/// <exception cref="System.InvalidOperationException">Collection is read only.</exception>
/// <exception cref="System.ArgumentNullException"><paramref name="item"/> may not be <see langword="null"/>.</exception>
public bool Remove(string item)
{
if (IsDisposed) throw new ObjectDisposedException(Name);
if (item == null) throw new ArgumentNullException(nameof(item));
if (IsReadOnly) throw new InvalidOperationException(Resources.Infra_Collections_ReadOnly);
if (!Contains(item)) return false;
return InternalRemoveWithChecks(item, null);
}
private bool InternalRemoveWithChecks(string itemName, T item)
{
if (item == null) item = m_Items[itemName];
OnRemove(item);
if (InternalRemove(itemName)) {
try {
OnRemoveComplete(item);
return true;
} catch {
m_Items.Add(itemName, item);
throw;
}
}
return false;
}
/// <summary>
/// Internally remove the item from the collection without raising events.
/// </summary>
/// <param name="item">The item identifier that should be removed.</param>
/// <returns><see langword="true"/> if the item was removed, <see langword="false"/> otherwise.</returns>
protected bool InternalRemove(string itemName)
{
if (itemName == null) throw new ArgumentNullException(nameof(itemName));
return m_Items.Remove(itemName);
}
/// <summary>
/// Called when before an item is removed from the collection.
/// </summary>
/// <param name="item">The item to be removed.</param>
protected virtual void OnRemove(T item) { }
/// <summary>
/// Called when the item has been removed from the collection.
/// </summary>
/// <param name="item">The item that was removed.</param>
/// <remarks>
/// This method is only called if the object was found in the collection and was removed.
/// </remarks>
protected virtual void OnRemoveComplete(T item) { }
#endregion
#region IDisposable
/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
/// <value>
/// <see langword="true"/> if this instance is disposed; otherwise, <see langword="false"/>.
/// </value>
protected bool IsDisposed
{
get { return m_Items == null; }
}
/// <summary>
/// Dispose all devices in this collection.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose all devices in this collection.
/// </summary>
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources;
/// <see langword="false"/> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed && disposing) {
foreach (T item in m_Items.Values) {
IDisposable disposableItem = item as IDisposable;
if (disposableItem != null) disposableItem.Dispose();
}
m_Items = null;
}
}
#endregion
}
}' not found to remove
Parameter name: syntaxTree'. RjBuildTest 1 Active |
The version of NuGet.Frameworks that I'm using is v4.6.2 and the target framework is .NET 4.5, so it's pulling in the version .NETFramework,Version=v4.0 |
I have tried a lot of combination but I really don't manage to reproduce the issue. Could you share a full project reproducing the error? |
Hi, do you have some suggestions that i can zip the project and provide privately? I could provide a second set also with lots of exceptions, bit can't post it here. |
Hi @jcurl, you could upload the zip file here. This is a Dropbox file request, an upload-only link, no one can download the files except me. |
@jcurl, thanks for reporting the issue. The bug is fixed in next version of SonarC#. FYI we had trouble reproducing the issue ourselves because it only occurs in the IDE and only if the base class and child classes are different projects. It was easy to repro with the projects you uploaded, so thanks again for that. |
Great! Glad to be of help. I've got another project with 54 of these warnings, so looking forward to the correction. |
Description
AD0001 internal error
Repro steps
Expected behavior
No error AD0001
Actual behavior
See above
Known workarounds
Ignore, at the peril of other errors not being tested
Related information
The text was updated successfully, but these errors were encountered: